FutureTask介绍
FutureTask是一个可以取消异步 任务的类。FutureTask对Future做的一个基本实现。可以调用方法去开始和取消一个任务。
一般是配合Callable去使用。
异步任务启动之后,可以获取一个绑定当前异步任务的FutureTask。
可以基于FutureTask的方法去取消任务,查看任务是否结果 ,以及获取任务的返回结果。
FutureTask内部的整体结构中,实现了RunnableFuture 的接口,这个接口又继承了Runnable , Future 这个两个接口。所以FutureTask也可以作为任务直接交给线程池去处理。
FutureTask应用
大方向是FutureTask对任务的控制:
* 任务执行过程中状态的控制
* 任务执行完毕后,返回结果的获取
FutureTask的任务在执行run方法后,是无法被再次运行 ,需要使用runAndReset方法才可以。
FutureTask源码分析
看FutureTask的源码,要从几个方向去看:
* 先查看FutureTask中提供的一些状态
* 在查看任务的执行过程
FutureTask中的核心属性
清楚任务的流转状态是怎样的,其次对于核心属性要追到是干嘛的。
java
/**
FutureTask的核心属性
FutureTask任务的状态流转
* NEW -> COMPLETING -> NORMAL 任务正常执行,并且返回结果也正常返回
* NEW -> COMPLETING -> EXCEPTIONAL 任务正常执行,但是结果是异常
* NEW -> CANCELLED 任务被取消
* NEW -> INTERRUPTING -> INTERRUPTED 任务被中断
*/
// 记录任务的状态
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 Object outcome; // non-volatile, protected by state reads/writes
/** 执行任务的线程 */
private volatile Thread runner;
/** 等待返回结果的线程Node对象, */
private volatile WaitNode waiters;
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
FutureTask的run方法
任务执行前的一些判断,以及调用任务封装结果的方式,还有最后的一些后续处理
java
// 当线程池执行FutureTask任务时,会调用的方法
public void run() {
// 如果当前任务状态不是NEW,直接return告辞
if (state != NEW ||
// 如果状态正确是NEW,这边需要基于CAS将runner属性设置为当前线程
// 如果CAS失败,直接return告辞
!UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
return;
try {
// 将要执行的任务拿到
Callable<V> c = callable;
// 健壮性判断,保证任务不是null
// 再次判断任务的状态是NEW(DCL)
if (c != null && state == NEW) {
// 执行任务
// result:任务的返回结果
// ran:如果为true,任务正常结束。 如果为false,任务异常结束。
V result;
boolean ran;
try {
// 执行任务
result = c.call();
// 正常结果,ran设置为true
ran = true;
} catch (Throwable ex) {
// 如果任务执行期间出了异常
// 返回结果置位null
result = null;
// ran设置为false
ran = false;
// 封装异常结果
setException(ex);
}
if (ran)
// 封装正常结果
set(result);
}
} finally {
// 将执行任务的线程置位null
runner = null;
// 拿到任务的状态
int s = state;
// 如果状态大于等于INTERRUPTING
if (s >= INTERRUPTING)
// 进来代表任务中断,做一些后续处理
handlePossibleCancellationInterrupt(s);
}
}
当FutureTask处于未启动或者已启动的状态时 ,调用FutureTask对象的get 方法会将导致调用线程阻塞。
当FutureTask处于已完成的状态时,调用FutureTask的get方法会立即放回调用结果 或者抛出异常 。
当FutureTask处于未启动 状态时,调用FutureTask对象的cancel 方法将导致线程永远不会被执行;当FutureTask处于已启动 状态时,调用FutureTask对象cancel(true)方法将以中断执行此任务的线程的方式来试图停止此任务;
当FutureTask处于已启动 状态时,调用FutureTask对象cancel(false)方法将不会对正在进行的任务产生任何影响;
当FutureTask处于已完成 状态时,调用FutureTask对象cancel方法将返回false ;
FutureTask的get和cancel的执行示意图
FutureTask使用
可以把FutureTask交给Executor 执行;也可以通ExecutorService .submit (...)方法返回一个 FutureTask,然后执行FutureTask.get()方法或FutureTask.cancel(...)方法。除此以外,还可以单独使用FutureTask。
当一个线程需要等待另一个线程 把某个任务执行完后它才能继续执行,此时可以使用FutureTask。假设有多个线程执行若干任务,每个任务最多只能被执行一次 。当多个线程试图同时执行同一个任务时,只允许一个线程执行任务 ,其他线程需要等待这个任务执行完后才能继续执行。下面是对应的示例代码。
java
private final ConcurrentMap<Object, Future<String>> taskCache = new ConcurrentHashMap<>();
private String executionTask(final String taskName)throws ExecutionException, InterruptedException {
while (true) {
Future<String> future = taskCache.get(taskName); // 1.1,2.1
if (future == null) {
Callable<String> task = () -> taskName;
FutureTask<String> futureTask = new FutureTask<>(task);
future = taskCache.putIfAbsent(taskName, futureTask); // 1.3
if (future == null) {
future = futureTask;
futureTask.run(); // 1.4执行任务
}
}
try {
return future.get(); // 1.5,
} catch (CancellationException e) {
taskCache.remove(taskName, future);
}
}
}
当两个线程试图同时执行同一个任务时,如果Thread 1执行1.3后Thread 2执行2.1,那么接下来Thread 2将在2.2等待,直到Thread 1执行完1.4后Thread 2才能从2.2(FutureTask.get())返回
FutureTask实现
jdk1.8的FutureTask有个说明:
修订说明:这与这个类以前依赖AbstractQueuedsynchronizer 的版本不同,主要是为了避免在取消竞争期间保留中断状态 让用户感到意外。在当前的设计中,Sync控件依赖于通过CAS 更新的"state"字段来跟踪完成,以及一个简单的Treiber堆栈来保存等待的线程。
风格注意:与往常一样,我们绕过了使用 AtomicXFieldUpdaters的开销 ,而是直接使用Unsafe。
Future
FutureTask实现了Future接口,Future接口有5个方法 :
1、boolean cancel (boolean mayInterruptIfRunning)
尝试取消当前任务的执行。如果任务已经取消、已经完成或者其他原因不能取消,尝试将失败。如果任务还没有启动就调用了cancel(true),任务将永远不会被执行。如果任务已经启动,参数mayInterruptIfRunning 将决定任务是否应该中断执行该任务的线程,以尝试中断该任务。
如果任务不能被取消,通常是因为它已经正常完成,此时返回false,否则返回true
2、boolean isCancelled ()
如果任务在正常结束之前被被取消返回true
3、boolean isDone ()
正常结束、异常或者被取消导致任务完成,将返回true
4、V get ()
等待任务结束,然后获取结果,如果任务在等待过程中被终端将抛出InterruptedException,如果任务被取消将抛出CancellationException,如果任务中执行过程中发生异常将抛出ExecutionException。
5、V get (long timeout, TimeUnit unit)
任务最多在给定时间内完成并返回结果,如果没有在给定时间内完成任务将抛出TimeoutException。
FutureTask状态转换
FutureTask有以下7中状态:
FutureTask任务的运行状态,最初为NEW。运行状态仅在set 、setException 和cancel 方法中转换为终端状态 。在完成过程中,状态可能呈现出瞬时值 INTERRUPTING(仅在中断运行程序以满足cancel(true) 的情况下)或者COMPLETING (在设置结果时)状态时。从这些中间状态到最终状态的转换使用成本更低 的有序/延迟写 ,因为值是统一的,需要进一步修改。
state:表示当前任务的运行状态,FutureTask的所有方法都是围绕state开展的 ,state声明为volatile,保证了state的可见性,当对state进行修改时所有的线程都会看到。
NEW:表示一个新的任务,初始状态
COMPLETING:当任务被设置结果时,处于COMPLETING状态,这是一个中间状态 。
NORMAL:表示任务正常结束。
EXCEPTIONAL:表示任务因异常而结束
CANCELLED:任务还未执行之前就调用了cancel(true)方法,任务处于CANCELLED
INTERRUPTING:当任务调用cancel(true)中断程序时,任务处于INTERRUPTING状态,这是一个中间状态 。
INTERRUPTED:任务调用cancel(true)中断程序时会调用interrupt()方法中断线程运行,任务状态由INTERRUPTING转变为INTERRUPTED
可能的状态过渡 :
1、NEW -> COMPLETING -> NORMAL:正常结束
2、NEW -> COMPLETING -> EXCEPTIONAL:异常结束
3、NEW -> CANCELLED:任务被取消
4、NEW -> INTERRUPTING -> INTERRUPTED:任务出现中断
知识来源: