FutureTask类实现了RunnableFuture接口,RunnableFuture接口继承了Future和Runnable两个接口,所以FutureTask可以当成线程执行。FutureTask持有一个Callable属性,FutureTask的两个构造方法都跟这个Callable属性相关。
第一个构造方法直接接收一个Callbale对象:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
第二个构造方法接收一个Runnable和一个Result对象:
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
实质上是调用Executors.callable方法将runnable和result封装成一个callable适配器对象,该适配器对象的call方法先调用runnable对象的run方法,run方法执行完毕之后直接返回result对象:
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
FutureTask的结果会保存在一个Object outcome属性中,这个值可能是正常的返回值,也可能是一个异常;它的get方法会根据运行的状态(state)来封装outcome。如果运行正常结束就将outcome转化为泛型的结果返回,如果线程被取消了就抛出一个CancellationException异常,否则就用ExecutionException封装outcome并抛出该异常;
FutureTask的结果是异步计算的,如果调用get的时候还没有计算完成那么调用get的线程就会被阻塞住,直到计算完成才会唤醒阻塞的线程。FutureTask内部通过一个简单的单链表记录所有调用get并被阻塞住的线程,计算完毕或者抛出异常后会通过循环遍历整个单链表唤醒所有阻塞的线程(park和unpark)。
前面说过FutureTask实现了RunnableFuture接口,间接实现了Runnable接口,所以它能够实现异步计算。它的run方法非常简单,只是调用内部的callable的call方法,如果正常执行完成就将state的状态设置为2(正常完结),并将outcome的值设置为callable的返回值。如果被取消或者中途抛出异常那么就将state修改为对应状态的值,并将异常记录到outcome中。最后通过state来对outcome进行处理并返回。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;
处理最终返回结果的代码如下:
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);
}