由于有获取异步任务执行结果的需求,所以java又扩展了一种Callable任务。
@FunctionalInterface
public interface Callable {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
一个典型的用法是:
public class Main {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
Future future = executorService.submit(() -> {
// 一个异步任务
return 0;
});
try {
// 获取异步结果
System.out.println(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
那么get是如何获取异步任务的结果的呢?
秘密就在FutureTask和AbstractExecutorService里。
经过这两个类的联动,就在Runnable任务的基础上,扩展出了Callable任务。
首先看,AbstractExecutorService的submit和newTaskFor:
public Future submit(Callable task) {
if (task == null) throw new NullPointerException();
RunnableFuture ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
protected RunnableFuture newTaskFor(Callable callable) {
return new FutureTask(callable);
}
Callable任务提交后,经FutureTask封装,其实真正异步执行的是FutureTask的run方法,最后submit返回的其实也是FutureTask对象。然后调用get方法获取异步任务的结果,其实调用的也是FutureTask的get方法。
那么重点就是,FutureTask是如何在run方法里执行异步任务,并在get方法里获取异步任务的结果?
其实靠的就是两者共享的一个中间变量,靠这个中间变量中转异步任务的执行结果。
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
// 这个callable就是newTaskFor时传进来的异步任务
Callable c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 执行Callable的call方法
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
// 如果发生异常,set异常
setException(ex);
}
if (ran)
// 任务没有异常,set执行结果
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
call的执行有两种情况,1. 发生异常,通过setException方法设置结果。2. 正常执行,通过set方法设置结果。
// outcome就是异步结果的中转变量
private Object outcome;
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
// 把Throwable对象放到outcome里
outcome = t;
// 终态置为EXCEPTIONAL
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
// 唤醒所有阻塞在get方法上的线程
finishCompletion();
}
}
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
// 正常的执行结果放入outcome
outcome = v;
// 终态置为NORMAL
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
// 唤醒所有阻塞在get方法上的线程
finishCompletion();
}
}
再来看看get方法
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
// 异步任务还没完成,阻塞当前想要获取结果的线程
s = awaitDone(false, 0L);
// 返回异步结果
return report(s);
}
// 参数s是任务执行状态
private V report(int s) throws ExecutionException {
// 根据outcome里的执行结果和任务执行状态,返回正常结果或抛出异常
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
总结:对于Callable任务来说,其实真正异步的还是Runnable任务,Callable只是对Runnable的封装,通过中间变量,让我们可以方便的获取异步执行的结果。