更好地理解与使用Future

一个多月没有写东西了,今天想写的也是想记录下来的一些学习及思考结果,记忆能力有限,避免时间长久就忘记了,今天想写的也仍是一些基础的东西,为何我老是关注这些平时码业务代码不多能用到的又比较基础东西呢,主要是由于我以为可能光写简单的业务代码可能不多有机会会遇到难题,可是也有万一,万一遇到了怎么解决,万变不离其宗,基础知识的深度决定上层建筑的高度,所以,扯正题吧,今天写的是关于JDK里面的Future。
多线程


Future模式

Future模式的核心思想是可以让主线程将原来须要同步等待的这段时间用来作其余的事情,这个时候能够更好的利用CPU分片,这点能够这么理解,若是咱们申请了线程,可是又没让CPU调度,这岂不是很浪费,咱们能够有更好的方法来提升CPU的利用率,也就是让线程原本能够等待(歇息)的时间让线程去作其余的事情,榨干它的剩余价值,固然是以CPU没有打满(100%利用率)为前提,JAVA里面对Future模式的具体实现是JDK1.5开始的JCU包中的Future接口及其实现的定义。工具

不一样的工做方式学习

上图简单描述了不使用Future和使用Future的区别,不使用Future模式,主线程在invoke完一些耗时逻辑以后须要等待,这个耗时逻辑在实际应用中多是一次RPC调用,多是一个本地IO操做等。B图表达的是使用Future模式以后,咱们主线程在invoke以后能够当即返回,去作其余的事情,回头再来看看刚才提交的invoke有没有结果。spa


JAVA中Future模式定义的行为

咱们来一块儿看看JAVA中Future接口的定义:线程

Future接口定义代理

接口定义行为,咱们经过上图能够看到实现Future接口的子类会具备哪些行为,假设咱们已经委托了系统一些执行逻辑,那么对于这个执行逻辑,咱们有:对象

  1. 咱们能够取消这个执行逻辑,若是这个逻辑已经正在执行,提供可选的参数来控制是否取消已经正在执行的逻辑。接口

  2. 咱们能够判断执行逻辑是否已经被取消。生命周期

  3. 咱们能够判断执行逻辑是否已经执行完成。队列

  4. 咱们能够获取执行逻辑的执行结果。

  5. 咱们能够容许在必定时间内去等待获取执行结果,若是超过这个时间,抛TimeoutException。


线程池中的FutureTask

在JCU中,FutureTask是Future的具体实现,额外实现了Runnable接口,既然实现Runnable接口,那么就知足了Task的行为,因而咱们获得了一个能够被用来执行的Future。值得一提的是FutureTask的身份,她是JCU提供的线程池实现用到的任务基本单元,经常使用线程池的同窗都知道,线程池接收两种对象,一个是Runnable任务,一种是Callable任务,二者区别在于前者返回执行结果给外部,后者须要。按照默认线程池是实现ExecutorService接口的,按照ExecutorService接口定义的行为,咱们能够将Runnable或Callable任务提交到线程池让其去被执行,而被提交的Runnable或Callable任务都会被包装成FutureTask,丢到任务队列,由线程池的工做线程去执行。


FutureTask任务状态流转

当咱们把一个FutureTask丢到线程池任务队列以后,任务后续的生命周期是怎么样的呢?在FutureTask中定义了七种任务状态,咱们能够一块儿看一下:

  1. NEW:当FutureTask被初始建立的时候的状态。

  2. COMPLETING:当任务被执行完毕,FutureTask会将执行结果设置给FutureTask的outcome属性,在设置以前会将FutureTask的状态修改成COMPLETING。

  3. NORMAL:当任务被执行完毕,FutureTask会将执行结果设置给FutureTask的outcome属性,在设置以后会将FutureTask的状态修改成NORMAL。

  4. EXCEPTIONAL:当任务在被执行的过程当中抛了异常,FutureTask会将异常信息设置给FutureTask的outcome属性,在设置以前会将FutureTask的状态修改成COMPLETING,在设置以后会将FutureTask的状态修改成EXCEPTIONAL。

  5. CANCELLED:当外部想要取消任务,而又不容许当任务正在执行的时候被取消的时候会将FutureTask的状态修改成CANCELLED。

  6. INTERRUPTING:当外部想要取消任务,同时容许当任务正在执行的时候被取消的时候,会先将FutureTask的状态设置为INTERRUPTING,而后设置执行任务的线程的中断标记位。

  7. INTERRUPTED:当外部想要取消任务,同时容许当任务正在执行的时候被取消的时候,会先将FutureTask的状态设置为INTERRUPTING,而后设置执行任务的线程的中断标记位,最后将Future的状态设置为INTERRUPTED。

综上,咱们也能够总结下FutureTask的状态流转可能流程:

  1. NEW—>COMPLETING—>NORMAL(任务执行正常)

  2. NEW—>COMPLETING—>EXCEPTIONAL(任务执行异常)

  3. NEW—>CANCELLED(不容许执行中的取消)

  4. NEW—>INTERRUPTING—>INTERRUPTED(容许执行中的取消)


总结

JCU包提供了很好的工具让咱们可以快速的开发基于线程池的多线程应用,那么当咱们把线程提交到线程池以后,站在单独任务的角度,咱们关心的核心问题一般是下面几点:

  1. 任务超时时间,一方面咱们不可以无限期的占用线程资源,另外一方面咱们不可以让外部无限期的等待,所以timeout变得尤其重要。

  2. 主动取消任务,假如咱们以为timeout不够灵活,一般场景是当咱们在timeout以前已经知道FutureTask不须要再继续为咱们工做的时候,咱们能够先判断任务是否已经done(isDone),若是没有done,咱们能够主动的将任务取消掉,这个时候Future定义的cancel能够派上用场。

  3. 任务异常信息,还记得咱们最初提交的Runnable和Callable么,当任务抛出了异常咱们如何get到异常信息呢,FutureTask实际上是代理了Runnable和Callable的执行,捕获异常并将异常信息交给outcome,所以经过FutureTask,咱们一样能够得到任务内部抛出的异常信息。


文章由博主原创,若有须要欢迎转载,若有错误多多包涵。

相关文章
相关标签/搜索