FutureTask继承于Future,一直到JDK1.8它都是Future的主要实现类,用于获取任务的执行结果。在JDK1.8的注释中是这样介绍这个类的。
这个类提供了一个Future的基本实现,它可以开始、取消一个任务(该任务是异步可取消的),或者查询该任务是否完成,并且可以获取任务执行的结果。这个结果只有当任务执行完成后才可被获取;获取任务结果通过它的get方法,并且该方法会被阻塞直到任务执行完成。一旦任务执行完成,它将不能被再次开始或者取消,除非通过runAndReset方法调用该任务。
FutureTask可以去包裹一个Runnable或者Callable对象,因为它实现了Runnable接口,也因此它可以被放在Executor中执行。
这个类除了作为一个功能完善的类对外提供服务之外,还提供了一些protected方法用于我们自定义一个继承于该类的类,来实现我们需要的功能。
下面我们就简单看一下FutureTask的源码。
源码分析
我们先看一下FutureTask的成员变量,源码如下:
1 | /** |
可以看到FutureTask中主要有上面5个成员变量,它们的用途可以参考注释。注意到WaitNode类型的变量,这是FutureTask的内部类,看命名我们可以猜测它是一个用于存储等待线程的队列,下面我们看一下它的源码。
1 | /** |
可以看到它确实就是一个记录线程引用的结构体,采用链表结构实现。
下面看一下FutureTask的构造函数,FutureTask有两个构造函数,可以分别包装Callable型和Runnable型的任务。它们的源码如下:
1 | /** |
可以看到这两个构造函数就是传入两种任务类型,并且初始化state为New。细心的读者可以看到第二个构造函数比第一个多传入了一个参数,这是因为Runnable接口不支持泛型,因此需要一个参数来指定它的结果类型。至于任务执行的结果是直接赋给这个引用,还是新建一个新的引用,在笔者看来并不重要,因为我们最终是通过FutureTask的get方法来得到它的。因此这里笔者认为主要的是它的类型,并使用这个类型通过Executors的工厂方法将Runnable转化为Callable。下面我们就看看具体的get方法实现。它的源码如下:
1 | /** |
代码中首先判断state是否小于等于COMPLETING,即若任务处于新建或者正在运行的状态时 ,调用awaitDone方法,我们看一下这个方法的实现。它的源码如下:
1 | /** |
上面就是awaitDone的执行流程,下面看一下report方法,它的源码如下:
1 | /** |
可以看到这方法只是判断任务是否正常执行,如果是,返回结果;如果不是,抛出异常。
读者根据上面的注释大致可以明白get方法的执行流程,但细心的读者可能会疑惑一个问题,上面的无论是awaitDone方法,还是report方法都是对成员变量的读,那么写是在哪里呢?FutureTask是在哪里给state赋予不同的状态,并将任务结果赋值给outcome的?毕竟在我们使用Future的过程,我们只要调用get方法就能获取到任务的执行结果了,上面的代码流程似乎也没有涉及state和outcome的改变,最让人疑惑的是FutureTask中也没有发布对这些改变的公有方法。
这里就需要理解Executor完整的执行流程,对state修改的入口是从Executor的execute方法开始的,当我们把任务放入execute方法时,它最终会调用到FutureTask的run方法,并在这里执行任务的run方法,并且改变状态。下面我们看一下FutureTask的run方法。它的源码如下:
1 | public void run() { |
下面看一下run方法中调用的其他方法。我们先看一下setException方法的源码。如下:
1 | /** |
如果state还是处于NEW状态,也就是没有被设置为其他状态,则设置当前状态为EXCEPTIONAL。并最终调用finishCompletion方法清理现场。下面我们看一下finishCompletion方法的源码。
1 | /** |
可以看到这个方法就是清理WaitNode,唤醒在get方法中挂起的线程,并将callable置为null。同时还可以看到他调用了一个done方法,我们看一下这个方法的源码,如下。
1 | /** |
可以看到这个方法什么都没干,这其实就是FutureTask提供的一个hooks,只是用于给开发者自定义使用的。如果FutureTask的finishCompletion满足不了我们的要求,那么我们就可以继承FutureTask,并实现done方法,做一些我们希望的处理。
下面看一下run方法中set方法,这是任务正常结束会调用的方法。下面看一下set方法的源码,如下:
1 | /** |
与setException不同的地方就是,它将任务状态设为NORMAL而不是EXCEPTIONAL。
run方法还要最后一个方法handlePossibleCancellationInterrupt,这个方法用于处理中断。它的源码如下,就是一个循环让步。
1 | /** |
到这里run方法就看完了,下面看一下FutureTask的其他发布方法。先看一下cancel方法,它的源码如下:
1 | public boolean cancel(boolean mayInterruptIfRunning) { |
这个方法传入了一个mayInterruptIfRunning参数,用于标识是否中断,true表示中断线程。它的执行流程如上面注释所说的。
FutureTask发布的方法还有两个监控方法,如下:
1 | public boolean isCancelled() { |
1 | public boolean isDone() { |
从这两个方法中我们可以看到,state的状态大于等于CANCELLED,就可以视为取消。即中断也可视为取消。state不为NEW就可以视为done。
总结
到这里FutureTask就介绍完了。本篇博客是边看源码边写成的,且笔者水平有限,难免有些谬误。读者在读到此篇博客时,若有疑惑,当自己反复查证。