
Java多线程(二):Callable&Future&FutureTask源码分析
1.创建线程任务方案一:Runnable
runnable无返回值,run实现线程逻辑
2.创建线程任务方案二:Callable
2.1 Callable
callable有返回值(V),call实现线程逻辑
2.2 Future
Callabe 不能单独使用,需要 Future 用来控制Callable执行,获取Callable执行结果。
get 方法主要作用是得到 Callable 异步任务执行的结果,无参 get 会一直等待任务执行完成之后才返回,有参 get 方法可以设定固定的时间,在设定的时间内,如果任务还没有执行成功,直接返回异常,在实际工作中,建议多多使用 get 有参方法,少用 get 无参方法,防止任务执行过慢时,多数线程都在等待,造成线程耗尽的问题。
cancel 方法主要用来取消任务,如果任务还没有执行,是可以取消的,如果任务已经在执行过程中了,你可以选择不取消,或者直接打断执行中的任务
那到这里就有一个问题了, Callable 与 Future都是接口,怎么实现通过Future控制Callable呢?可以创建一个中间类实现Future接口,然后将Callable组合进来,最后通过Future接口中的方法实现控制。这里的具体逻辑可以在FutureTask中看到。
3.FutureTask
Runnable 和 Callable 都可以表示线程要执行的任务,那么这两个接口如何在不该变有关系的基础上互相转化?
首先,Runnable 与 Callable 肯定不能通过extends实现,因为直接继承就是is-a的关系,而这俩显然不是
那么还有一个办法,引入一个中间类 F(满足以下两个条件)
F extends Runnable:继承Runnable
F { primary Callable c }:组合Callable
==> 最终,结合上面的所有分析,我们得到了 FutureTask 的结构:实现Future接口 & 实现Runnable接口 & 组合Callable
3.1 RunnableFuture、
首先来看一下 FutureTask 实现的 RunnableFuture 接口是什么
继承Runable:将外在变成Runnable
继承Future:实现Future对Callable的控制
下面我们就分别看看FutureTask是如何实现Future接口和Runnable接口中的方法
3.2 Future接口方法的实现
通过实现Future,组合Callable实现了两者的结合
构造器传入Callable
实现Future的方法进行返回结果操作
FutureTask实现了Runnable,当构造函数传入Callable就直接转化为了Runnable
get
get 有无限阻塞和带超时时间两种方法,我们通常建议使用带超时时间的方法,源码如下
cancel
取消任务,如果正在运行,尝试去打断
3.3 Runnable接口方法的实现
run
run 方法可以直接被调用,也可以开启新的线程进行调用
run 方法是没有返回值的,通过给 outcome 属性赋值(set(result)),get 时就能从 outcome 属性中拿到返回值;
FutureTask 两种构造器,最终都转化成了 Callable,所以在 run 方法执行的时候,只需要执行 Callable 的 call 方法即可,在执行 c.call() 代码时,如果入参是 Runnable 的话, 调用路径为 c.call() -> RunnableAdapter.call() -> Runnable.run(),如果入参是 Callable 的话,直接调用。
3.4 runnale转化成callable
构造函数传入Runnable与返回值,转化后赋给callable
适配器模式:Executors.callable() --> RunnableAdpter
最后,总结一下FutureTask 有什么作用:
实现了 Future 的所有方法,对任务有一定的管理功能,比如说拿到任务执行结果,取消任务,打断任务等等。
组合了 Callable,实现了 Runnable,把 Callable 和 Runnnable 串联了起来。
统一了有参任务和无参任务两种定义方式,方便了使用。
作者:A minor
来源:CSDN
