FutureTask提供的主要功能
1、(超时)获取异步任务完成后的执行结果;
2、判断异步任务是否执行完成;
3、能够取消异步执行中的任务;
4、能够重复执行任务;
源码分析FutureTask的功能
FutureTask其实类似一个代理机构,当我们提交任务的任务执行时,其实是由这个代理机构为我们触发的此任务,而且也会维护任务的结果、异常信息及任务执行过程中的状态。
代码使用示例:
public class ThreadPoolConfig {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask(() -> {
Thread.sleep(1000 * 3);
return "done";
});
new Thread(futureTask).start();
String result = futureTask.get();
System.out.println(result);
}
}
当我们提交任务时:
FutureTask这个代理会保存我们的具体任务,同时初始化任务的执行状态:
this.callable = callable;
this.state = NEW;
/** The underlying callable; nulled out after running */
private Callable<V> callable;
private volatile int state;
当线程真正的执行时:
代理被线程调度执行,最终代理会执行我们的任务:
result = c.call();
ran = true;
任务执行完后,会保存任务的执行结果或异常信息及更新任务的执行状态。
当任务执行成功时,保存任务的结果到变量:
/** The result to return or exception to throw from get() */
private Object outcome; // non-volatile, protected by state reads/writes
同时任务的执行状态:
private static final VarHandle STATE;
当任务还未执行完毕时候,我们获取任务结果时,会阻塞:
java.util.concurrent.FutureTask#get()
java.util.concurrent.FutureTask#get(long, java.util.concurrent.TimeUnit)
如果任务的执行状态还在执行中,就会阻塞当前线程。任务执行完会更新任务的执行状态,并且唤醒被阻塞的线程。
任务结束时,需要把任务的结果值或异常保留在当前FutureTask的outcome中。
FutureTask有哪些坑
1、不调用get方法获取结果,可能永远也不知道异常信息
任务中发生的异常会保存在FutureTask中,忽略获取结果,我们可能永远丢失异常信息。
2、不用带超时的get方法获取结果,可能永远会被阻塞
在线程池中,使用
java.util.concurrent.ThreadPoolExecutor.DiscardPolicy
中的默认实现,会使的FutureTask的任务状态永远不更新,非超时get方法会永远阻塞。
小结
其实FutureTask只是我们任务的代理,会记录任务执行的结果及异常信息,并提供阻塞唤醒机制来实现线程的阻塞与等待。
并且获取结果时候,不带超时的get方法可能导致异常信息丢失,或者一直被阻塞的情况。