Callable 与Runnable 接口对比
Runnable 是个接口,只有一个方法run(),实现Runnable的run(),把要实现的逻辑写到run()中,然后启动线程就行;
Callable 代码也很简单,不同的是,Callable 是个泛型接口,call(),返回类型是传入的泛型,Callable 与Runnable大致相似,Callable 功能更强大写,主要是Callable 执行后有返回值,并且能够抛出异常
Future 接口
对于具体的Callable 或者Runnable任务,它可以进行取消,查询任务是否被取消,查询是否完成,以及获取结果等等,通常都是异步执行的,不能直接从别的线程直接得到方法返回值,Future可以监视目标线程调用call的情况,当调用future的get方式时,可以获取线程结果,这时线程不会直接完成,当前线程就开始阻塞,知道call 方法返回结果 ,才继续执行,总之,Future可以获取其他线程的返回值。

执行结果:

FutureTask

FutureTask的父类是RunnableFuture ,RunnableFuture 继承了Runnable, Future,由此我们知道FutureTask 也可执行Callable类型的任务,如果构造函数参数是Runnable的话,它会转换成Callable类型,FutureTask 实现了Runnable, Future两个接口,所以FutureTask 既可以作为Runnable被线程执行, 又可以作为Future,得到Callable的返回值, 这个组合使用的好处:假如有个很费时的逻辑需要被计算,并且返回这个值,同时,这个值又不是马上需要,用另外一个线程去计算这个操作,当前线程可以做其他操作,等到需要这个返回值时,在用future得到。

执行结果:

FutureTask原理
在J.U.C提供的Future模式中,最重要的就是FutureTask类,FutureTask是在JDK1.5时,随着J.U.C一起引入的,它代表着一个异步任务,这个任务一般提交给Executor执行,当然也可以由调用方直接调用run方法运行。

既然是任务,就有状态,FutureTask一共给任务定义了7种状态:

NEW:表示任务的初始化状态;
COMPLETING:表示任务已执行完成(正常完成或异常完成),但任务结果或异常原因还未设置完成,属于中间状态;
NORMAL:表示任务已经执行完成(正常完成),且任务结果已设置完成,属于最终状态;
EXCEPTIONAL:表示任务已经执行完成(异常完成),且任务异常已设置完成,属于最终状态;
CANCELLED:表示任务还没开始执行就被取消(非中断方式),属于最终状态;
INTERRUPTING:表示任务还没开始执行就被取消(中断方式),正式被中断前的过渡状态,属于中间状态;
INTERRUPTED:表示任务还没开始执行就被取消(中断方式),且已被中断,属于最终状态。
各个状态之间的状态转换图如下:

上图需要注意的是两点:
- FutureTask虽然支持任务的取消(cancel方法),但是只有当任务是初始化(NEW状态)时才有效,否则cancel方法直接返回false;
- 当执行任务时(run方法),无论成功或异常,都会先过渡到COMPLETING状态,直到任务结果设置完成后,才会进入响应的终态。
JDK1.7之前,FutureTask通过内部类实现了AQS框架来实现功能。 JDK1.7及以后,则改变为直接通过Unsafe类CAS操作state状态字段来进行同步。
构造
FutureTask在构造时可以接受Runnable或Callable任务,如果是Runnable,则最终包装成Callable:


上述的Executors.callable()方法,其实就是对Runnable对象做了适配,返回Callable适配对象——RunnableAdapter:


FutureTask的字段定义非常简单,State标识任务的当前状态,状态之间的转换通过Unsafe来操作,所有操作都基于自旋+CAS完成:
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
private Callable<V> callable; // 真正的任务
private volatile Thread runner; // 保存正在执行任务的线程
/**
* 记录结果或异常
*/
private Object outcome;
/**
* 无锁栈(Treiber stack)
* 保存等待线程
*/
private volatile WaitNode waiters;
注意waiters这个字段,waiters指向一个“无锁栈”,该栈保存着所有等待线程,我们知道当调用FutureTask的get方法时,如果任务没有完成,则调用线程会被阻塞,其实就是将线程包装成WaitNode结点保存到waiters指向的栈中:


任务的运行
FutureTask的运行就是调用了run方法:

上述方法,首先判断当前任务的state是否等于NEW,如果不为NEW则说明任务或者已经执行过,或者已经被取消,直接返回。
正常执行完成后,会调用set方法设置任务执行结果:

如果任务执行过程中抛出异常,则调用setException设置异常信息:

任务的取消
cancel方法用于取消任务,参数mayInterruptIfRunning如果为true,表示中断正在执行任务的线程,否则仅仅是将任务状态置为CANCELLED :

任务取消后,最终调用finishCompletion方法,释放所有在栈上等待的线程:

结果获取
FutureTask可以通过get方法获取任务结果,如果需要限时等待,可以调用get(long timeout, TimeUnit unit)。

可以看到,如果当前任务的状态是NEW或COMPLETING,会调用awaitDone阻塞线程。否则会认为任务已经完成,直接通过report方法映射结果:

report会根据任务的状态进行映射,如果任务是Normal状态,说明正常执行完成,则返回任务结果;如果任务被取消(CANCELLED或INTERRUPTED),则抛出CancellationException;其它情况则抛出ExecutionException。

ScheduledFutureTask
ScheduledFutureTask是ScheduledThreadPoolExecutor这个线程池的默认调度任务类。
ScheduledFutureTask在普通FutureTask的基础上增加了周期执行/延迟执行的功能。通过下面的类图可以看到,它其实是通过继承FutureTask和Delayed接口来实现周期/延迟功能的。


ScheduledFutureTask的源码非常简单,基本都是委托FutureTask来实现的,关键是看下运行任务的方法:

FutureTask的runAndReset方法与run方法的区别就是当任务正常执行完成后,不会设置任务的最终状态(即保持NEW状态),以便任务重复执行:

Callable与Runnable深入解析

本文详细对比了Callable与Runnable接口,介绍了Future接口的功能,重点解析了FutureTask的原理,包括其状态转换、构造、运行、取消及结果获取过程,以及ScheduledFutureTask的周期/延迟执行特性。
1万+

被折叠的 条评论
为什么被折叠?



