序言
FutureTask是Future和Runnable的实现,ThreadPoolExecutor在执行任务的时候,执行的是FutureTask. 传统Runnable接口实现的任务只有执行方法run,并没有任务取消,执行超时等功能,并且Runnable并没有提供任务状态的抽象,其实每个任务都是有状态的。所以FutureTask其内部将任务执行过程分为一系列状态,从而使得任务有了生命周期。在JDK中,经典的实现除了FutureTask外,还有ScheduledFutureTask.
结构

我们可以看到它对一个普通任务支持了生命周期的方法. 从而使得任务执行有了过程的概念,而不是Runnable这样只能运行或者被中断的状态,也使得客户端更加灵活的控制任务执行。
API
FutureTask的API全部来自父接口,自己只定义了构造函数,
1 | //任务执行方法,继承自RunnableFuture的run,RunnableFuture又继承在Runnable |
实现
创建
1 | //通过Callable创建FutureTask,并且任务状态设置为NEW |
1 | //通过Runnable创建FutureTask,并且任务状态设置为NEW |
等待节点WaitNode
等待节点是当有多个线程获取结果的时候,会进行排队,当有一个线程get到结果时候,其他线程将被唤醒,也将拿到结果。该等待节点的实现是Treiber Stack,Treiber 是发明者名字,它是非阻塞的同步栈,详情可参考Wikipedia. https://en.wikipedia.org/wiki/Treiber_stack
1 | static final class WaitNode { |
该类的实现是静态final类,意味着这是一个全局的类,和外部实例没有关系,并且不能被继承,
实例变量
1 | /** 运行的任务 */ |
任务状态
1 | private volatile int state; //任务状态,每个API都会和状态相关 |
任务的状态有7种,每种任务状态是递增且不可逆的。下面是状态流转图:
任务起始状态是NEW,中间过程有COMPLETING和INTERRUPTING,终态有四种,也就是图的叶子节点。这些状态使得任务可以被控制。
任务运行run
任务运行是实现run方法,也就是客户端自定义的任务。
run方法首先判断状态,如果任务状态不NEW,则直接退出,防止任务重复执行,然后进入真正任务执行,调用Callable的call方法,
call结束,任务执行完成,将ran置为ture,正常情况调用set,如果运行中发生异常,调用setException,
1 | public void run() { |
如果发生异常将任务状态设置为EXCEPTIONAL
1 | protected void setException(Throwable t) { |
如果正常执行完成,将任务状态设置为NORMAL
1 | protected void set(V v) { |
获取任务结果get()
1 | public V get() throws InterruptedException, ExecutionException { |
从API可以看出,获取任务结果时候,任务可能被中断,或者发生执行异常。
awaitDone 自旋等待结果
读这段代码时候,一定要想着会有多个线程来awaitDone,并且每一个线程都在自旋,等待状态变化。每个线程按照排队方式排列在waiters进行等待。
假设有四个线程同时获取结果,每一个运行1s后,才启动另一个线程,那么每个线程第一次进入awaitDone时候将会创建自己的WaitNode,然后第二次进入会发现queued=false,然后将第一次进入的创建WaitNode节点next指向waiters,如Thread1 -> waiters,
第三次进入时候,因为所有的分支条件只满足最后一个,调用LockSupport.park(this),此时该线程因为一直没有获取结果而进行wait,此时线程状态变成waiting。依次类推,第二个线程进入,第三个线程进入,第四个线程进入,将会形成以下结构:
1 | //timed false说明没有超时时间限制 |
finishCompletion 完成任务
当有任务完成时候,会将Tribie Stack等待的线程全部unpark,并且释放每个WaitNode的线程.
1 | private void finishCompletion() { |
1 | ("unchecked") |
带超时的get()
这块和get()其实差不多,只是会进入get的不同for(;;)分支,当超过指定时间没有返回结果时候,将会抛出TimeoutException异常。
1 | public V get(long timeout, TimeUnit unit) |
取消任务cancel
任务取消成功返回true,取消失败返回false,可以从条件判断中得知,当状态为NEW,且被原子更新为INTERRUPTING或CANCELLED,
才能取消任务。当可以中断时候,任务通过中断实现的,中断之后将任务状态设置为INTERRUPTING,当不可以中断,任务取消其实并没有做什么,只是将任务状态修改为或CANCELLED,当任务状态发生变化时候,一直自旋等待线程会在get方法中获得状态变化,从而执行相关分析,最后执行finishCompletion.
1 | public boolean cancel(boolean mayInterruptIfRunning) { |
是否取消isCancelled
根据状态判断,因为状态是递增的。
1 | public boolean isCancelled() { |
是否完成isDone
同样根据状态判断。
1 | public boolean isDone() { |