FutureTask源码分析

 

1 前言介绍:

Future接口:

FutureTask实现了两个java接口,Runnable和Future。从代码注释可以看到Future接口被设计来执行异步任务,提供了检查计算是否完成(isDone()方法)、等待计算完成以及检索计算结果的方法(get()方法)。任务结果定义在实例化Future时传入。

     1. get()方法:

可以从注释中看到接口设计get()方法的处理方式为:调用Future实例的get()方法来阻塞主线程等待异步任务的执行结果,直到get()方法返回计算结果或者发生异常。

     2. get(long timeout, TimeUnit unit):

   设置最大等待时间

     3. isDone():

任务完成返回True, 完成可能是由于正常终止、异常或取消——在所有这些情况下,此方法将返回True。

     4. isCancelled():

如果任务在正常完成前被取消返回True。

     5. cancel(boolean mayInterruptIfRunning)

       尝试取消任务,如果任务还未被执行,取消后就不应该再被执行了,如果任务已完成、已取消或由于其他原因无法取消,则此尝试将失败。如果任务已经启动且现在在执行中,由mayInterruptIfRunning参数决定是否中断任务。

2 类架构

        FutureTask维护一个等待它执行完成的被阻塞线程链表,FutureTask完成后(正常完成或因为取消、异常而终止)会去解除所有阻塞的线程,并释放链表。

        注释中说明:本类为一个可被取消的异步计算任务,实现Future接口的功能以及实现Runnable接口,调用get方法获取任务执行结果会阻塞至异步任务执行完成。本类中定义了runAndReset方法,在任务达到完成或被取消状态后可以重新设置状态,再次被执行。

                                                                                     FutureTask UML图

3 类成员

3.1状态表示字段state

 

                                                       FutureTask状态变迁图

注释说明:任务状态初始为NEW,运行状态的转换只有三个方法可以做,分别是set()、setException()、cancel()。任务执行期间FutureTask可能会瞬间状态为COMPLETING,直到任务完成结果被set()方法设置;或者瞬间状态为INTERRUPTING,只有在调用cancel(true)方法后,正在中断任务的过程中。中间状态到最终状态由开销较小的顺序/延迟写,因为状态值的唯一性,且中间状态到最终状态之间没有值了,意思就是从中间状态只能修改到最终状态。

3. 2 任务承载对象callable

使用Future要实例化一个Callable对象,Future有两个构造函数,一个是传入一个Callable对象,另一个传入Runnable对象,构造函数中会使用Executors. callable()方法来实例化一个Callable对象,然后赋值给成员变量callable。

Callable接口和Runnable接口十分类似,Callable只有一个call()方法,类似Runnable接口的run()方法,都无参,但是call()方法有返回结果并且可以抛出异常。

3.3 任务执行结果outcome

异步任务的执行结果,调用get()方法返回此值或者抛出异常,这个变量的读写都受到state的保护,根据状态字段来决定是否可读、可写。

3.4 执行callable的线程runner

                   执行callable的线程。内部赋值都用Unsafe类的compareAndSwap方法,是native方法,是线程安全的。为啥操作任务对象中的值都采用CAS,我理解本意是为了省去加锁的操作,更为高效。

3.5 waiters

         记录等待异步任务的线程链表头节点,线程调用FutureTask的get方法后会把线程阻塞,记录到这个链表中,每次采用头插法,链表操作也都是用Unsafe类的CAS方法。在任务结束后会通知所有waiters链表中所有等待的线程,并且释放这个链表的引用。

4 构造函数

FutureTask类提供了两个构造函数:

  1. 使用Callable实例化对象作参数

构造函数设置callable变量,并将FutureTask状态设置为NEW。

  1. 使用Runnable实例化对象作参数

入参为Runnable对象,构造函数也会使用Executors工厂类构造一个Callable实例化对象。因为Callable接口定义的抽象方法call()有返回值,但是Runnable接口定义的run()方法没有返回值,所以入参有个result,可以看到Executors工厂类中构造的Callable对象直接返回这个result。

5 源码分析

FutureTask的使用方式一般为一下demo所示:

想要知道FutureTask如何异步执行任务, FutureTask的get()方法如何阻塞的等待异步任务计算完成。

5.1 get()

这个方法是实现了Future接口中定义的方法。函数只有短短4行,主要调用两个方法awaitDone()和report(),get()方法流程很简单,调用FutureTask的get()方法,判断FutureTask状态,如果处于NEW或COMPLETING状态,调用awaitDone()方法阻塞的等待异步任务结束或被中断,并设置任务状态,调用report()方法来根据状态字段决定是否正常返回任务执行结果或者抛出异常,这两个方法后面会详细介绍。

5.2 get(long timeout, TimeUnit unit)

           这个方法也是实现了Future接口中定义的方法。这个方法和上个get的区别就是设置阻塞等待异步任务的时间,超时了就不等了。

5.3 等待任务执行结束方法awaitDone(boolean timed, long nanos)

        注释说明:等待任务完成,或因为中断而终止,或超时。参数也很好理解,timed表示是否要设置等待超时,nanos表示任务要等待的纳秒。

 

 

      调用这个方法后线程会被阻塞在while(True)循环内,等待超时或finishCompetion()方法解除任务的阻塞状态,最终根据任务状态来判断任务是否正常执行,返回结果或抛出异常。

5.4 run()方法

 

    

                                                                      run()方法流程图

      使用过Thread thread = new Thread(Runnable runnable)这中写法的人都知道,Runnable中的run()方法会被新起的线程执行。本方法处理流程简单描述如下:

      如果FutureTask任务状态为NEW才会执行整个方法流程,方法中调用Callable的call()方法,并根据call()方法执行中是否正常结束来设置任务执行结果和状态。如果call()方法执行中正常结束,调用set(V result)方法来设置任务结果和状态,如果任务未被取消会把任务状态设置为NORMAL,并把任务执行结果设置给成员变量outcome,最后调用finishCompletion()方法,通知所有被阻塞的等待异步任务执行完成的线程。从set()方法中可以看到任务状态会在Callable.call()方法正确执行并返回后才会被设为COMPLETING。

 

5.5 set(V)方法

         该方法只会被run()方法调用,在run()方法中会执行Callable.call(),并在call()正常返回后被调用来设置任务状态:NEW->COMPLETING->NORMAL。可以看到COMPLETING真的如类注释所述,是一个瞬态,将任务状态从NEW->COMPLETING只有set和setException两个方法。

5.6 setException()方法

         当执行run()或runAndReset()方法时会调用setException()方法来处理Callable.call()抛出异常的情况,和set()方法的流程非常像,不过将outcome设置为异常对象,将任务状态改为EXCEPTIONAL,最后调用finishCompletion()来通知解除所有等待线程的阻塞,释放等待链表。

5.7 finishCompetion()

用于callable的call()方法执行结束后或任务被取消后,解除所有等待线程的阻塞,释放等待线程节点链表。调用get()方法线程后会在阻塞在awaitDone()方法中,被解除阻塞状态后会根据FutureTask的状态字段来决定返回结果或抛出异常。

 

5.8 cancel(boolean mayInterruptIfRunning)方法

 

 

 

                                                                       

                                                                         cancel()流程图

 

5.9 runAndReset()用于重复执行任务

        这个方法和run()方法极度相似,区别在于callable.call()方法正确执行并返回后,不设置返回结果也不设置任务状态,如果无异常、取消、中断,state不会被更新,一直保持为NEW。

        这个方法设计为实现任务可以被重复执行的情况,SchduledThreadPoolExecutor中使用的ScheduledFutureTask即为FutureTask的子类,其run()方法中判断如果是周期任务就调用此方法来执行任务。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值