FutureTask 源码分析
介绍
FutureTask 是一个可以取消的异步任务。它的计算通过 Callable 来实现,使用 FutrueTask 的优势在于:
- 可以获取线程执行后的返回结果
- 提供了超时控制功能
同时实现了 Runnable 以及 Future 接口
源码分析
FutureTask 的状态
NEW、COMPLETING、NORMAL、EXCEPTIONAL、CANCELLED、INTERRUPTING、INTERUPTED
FutureTask 内部结构
其中 outcome 是结果(包括正确执行的结果、异常、cancel 等),runner 为执行 callable 任务的线程、waiters可以理解过一个 stack,因为在调用 get 方法的线程放入 waiters 中。
public class FutureTask<V> implements RunnableFuture<V> {
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
/** The underlying callable; nulled out after running */
private Callable<V> callable;
/** The result to return or exception to throw from get() */
private Object outcome; // non-volatile, protected by state reads/writes
/** 执行callable的线程 **/
private volatile Thread runner;
/**
* Treiber stack of waiting threads
* 使用Treiber算法实现的无阻塞的Stack,
* 用于存放等待的线程
*/
private volatile WaitNode waiters;
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
public V get() throws InterruptedException, ExecutionException {
...
}
public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
...
}
FutureTask 的构造方法
有两个构造方法,一个是为 Callable 类型的入参,一个是传入一个 Runnable 对象以及 Result 结果对象,因为 Runnable 对象没有返回值,所以需要通过结果对象获取返回值,本质也是将这两个参数通过包装类实现 Callable 的接口。
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW;
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW;
}
run 方法
因为 FutureTask 本身实现了 Runnable 接口,因此需要实现 run 方法,在该类的 run 方法里面,本质是去执行了 callable 的 call 方法。
run 方法执行的过程:
- 只有 state 为 NEW 的时候才执行任务
- 执行前设置 runner 为当前线程、使用 CAS 设置是为了防止竞争
- 然后执行 Callable 对象的 call 方法
- 如果执行任务的过程中发生异常,捕获并设置异常
set 方法
设置返回结果,现将状态改为 COMPLETEING,然后在设置微 NORMAL
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
finishCompletion方法
由于任务的执行是异步的,因此其他线程通过 get 方法去获取返回值的时候可能此时结果还没有计算完成,会被阻塞加入到 WaitNode 节点中。当set 方法设置返回结果后,需要唤醒在等待结果的线程,获取返回值。
private void finishCompletion() {
// assert state > COMPLETING;
// 执行该方法时state必须大于COMPLETING
// 逐个唤醒waiters中的线程
for (WaitNode q; (q = waiters) != null;) {
// 设置栈顶节点为null
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
Thread t = q.thread;
// 唤醒线程
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
// 如果next为空,说明栈空了,跳出循环
WaitNode next = q.next;
if (next == null)
break;
// 方便gc回收
q.next = null; // unlink to help gc
// 重新设置栈顶node
q = next;
}
break;
}
}
// 钩子方法
done();
callable = null; // to reduce footprint
}
get 方法
有两个方法,第二个方法提供了超时控制,如果在规定时间内任务还未执行完毕或者状态还是 COMPLETEING,则抛出 TimeoutException。第一个方法会一直阻塞直到获取结果。
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
if (unit == null)
throw new NullPointerException();
int s = state;
if (s <= COMPLETING &&
(s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
throw new TimeoutException();
return report(s);
}
awaitDone方法
awaitDone方法的工作是根据状态来判断是否能够返回结果,如果任务还未执行完毕,要添加到waiters中并阻塞,否则返回状态。
其中阻塞到期时间用的方法为:LockSupport.parkNanos(this, nanos); 没有阻塞的方法为: LockSupport.park(this);
removeWaiter方法
这个方法在 awaitDone 方法被调用,主要用于处理中断或者到期的 get() 请求。通过将当前节点的 thread 设置为 null,然后遍历栈,找到该节点并移除。
private void removeWaiter(WaitNode node) {
if (node != null) {
// 将thread设置为null是因为下面要根据thread是否为null判断是否要把node移出
node.thread = null;
// 这里自旋保证删除成功
retry:
for (;;) { // restart on removeWaiter race
for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
s = q.next;
// q.thread != null说明该q节点不需要移除
if (q.thread != null)
pred = q;
// 如果q.thread == null,且pred != null,需要删除q节点
else if (pred != null) {
// 删除q节点
pred.next = s;
// pred.thread == null时说明在并发情况下被其他线程修改了;
// 返回第一个for循环重试
if (pred.thread == null) // check for race
continue retry;
}
// 如果q.thread != null且pred == null,说明q是栈顶节点
// 设置栈顶元素为s节点,如果失败则返回重试
else if (!UNSAFE.compareAndSwapObject(this, waitersOffset,
q, s))
continue retry;
}
break;
}
}
}
cancel方法
总得来说任务一但开始执行,那么久不允许取消,返回 false。
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}
report方法
get方法在调用awaitDone方法后,会调用report方法进行返回。
根据状态判断,有三种返回值的情况:
- 如果状态为 s == NORMAL 表示 outcome 为正常返回的结果
- 如果状态 s >= CANCELED 表示任务被取消了,因此抛出CancellationException
- 如果状态 s < CANCELED 状态只能是 EXCEPTIONAL,表示任务在执行过程中出现了异常,此时 outcome 为异常结果,因此抛出ExecutionException
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}