首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

有返回结果的异步任务Futrue与Callable

上一篇讲解了线程池execute方法详细执行过程,execute接受Runnable参数但没有返回执行结果,在需要有执行结果的地方就不太适用了。

异步任务有返回值

如果出现一个计算量稍大用时较长而后面需要结果的情况,可以先把这个任务提交到另外的线程异步执行,主线程仍然继续做运行等到需要计算结果的时候再来获取,但是提供异步计算Runnable或者说线程Thread都不返回结果。

但Java中提供了Future与Callable来实现可以获取异步执行结果的一套框架,在上一篇文章中线程池的execute方法没有返回值,但是线程池提供的submit方法支持返回值,而submit利用Future与Callable实现

Future与Callable简单解释

Callable接口声明了一个方法call提供一个泛型的返回结果,

Future接口声明了5个接口:

cancel方法:取消任务,如果任务取消成功则返回true,如果任务取消失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则都返回false。;。

isCancelled方法:表示任务是否已经被取消。

isDone方法:表示任务是否已经完成,若任务完成,则返回true;

get()方法:用来获取任务结果,这个方法会阻塞线程,会一直等到任务执行完毕才返回;get(long timeout, TimeUnit unit)方法如果在指定时间内,还没获取到结果,就直接返回null。

再看线程池的submit

线程池提供了三个submit都继承至AbstractExecutorService的实现,源码如下图:

三个方法接受不同的参数,但都会作为newTaskFor方法的参数生成一个RunnableFuture类的对象,所以最关键在于RunnableFuture类。

而RunnableFuture也简单是一个接口并且继承了Runnable与Future,所以它可以被当作Runnable用在线程池的execute方法中,而同时又拥有Future的所有功能。

而submit通过newTaskFor生成的实际上是RunnableFuture子类,通过看源码实际上生成的是RunnableFuture的实现类FutureTask,最终的关键就是FutureTask类。

最关键类FutureTask详解

FutureTask实现RunnableFuture,RunnableFuture继承Runnable与Future,所以可以当作Runnable在线程池中使用,作为execute方法的参数,同时它也是Future的真正实现者,它的关键属性如下图:

前面提到的Future几个方法在FutureTask中得到了实现,实现也很简单,源码如上图FutureTask维护一个状态state,来表示任务状态取消、中断、完成等。

我们都是通过Future的get方法来获取结果,而FutureTask对get方法实现也简单:

首先判断state的状态,如果大于3则抛出异常;

等于3则说明已经完成会执行Thread.yield()让出CPU;

小于3则说明任务还没有完成,就会把线程组装成WaitNode加入到waiters中,并中断当前线程

从上一篇知道execute最终是执行参数的run方法,在这里也就是FutureTask的run方法执行过程如下:

首先执行callable的call方法获取到返回结果;

CAS方式把状态从0设置为1;

如果设置成功(保证线程安全)把结果赋值给outcome;

第四步把状态从1设置为2,表示完成;

最后唤醒waiters链表中的线程

总结

可以看到有返回结果的异步实现最终依赖FutureTask,它同时实现了Runnable与Future,拥有一个Callable属性。get方法根据FutureTask的状态会把线程挂起并放到等待链表中了。

同时它可以被用到线程池中被执行,在线程池中最后会调用它的run方法,run方法会调用Callable的call方法也就是真正计算的方法,返回结果后会修改FutureTask的状态并唤醒等待链表的线程

线程池的submit方法还支持Runnable参数,但是FutureTask执行的是Callable的call方法,那么Runnable中的run是怎么转换成Callable的call方法呢?

实际上也很简单,采用适配器模式,建一个Callable的子类RunnableAdapter,它保存一个Runnable属性,RunnableAdapter的call方法调用Runnable的run方法,至于返回的结果为null或者自己传一个结果。

所以要实现有返回结果的异步任务,要么实现Callable并实现call方法,或者创建一个Runnable的实现类也行。

Java程序员日常学习笔记,如理解有误欢迎各位交流讨论!

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200830A0IUBG00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券