Future简介
1 Future接口
Future
接口表示异步计算的结果,一旦计算完成,计算将不能再取消。整个Future
的继承体系结构如下:
代表异步结算结果。该接口提供方法:
- 检查计算是否完成(isDone)
- 等待计算完成(get)
- 检索计算结果(get)
- 取消任务(cancel)。
cancel()方法只是试图取消任务执行。若已经被取消或计算已经完成,将不能再被取消。该方法返回后,isDone()和isCancelled()方法将会始终返回true
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
Future
接口通常配合Executor
接口来实现,如下:
// 方式1:Future接口实现
Future<String> future = Executor.submit(new Callable<String>() {
public String call() {
return doSomething();
}
});
// 方式2:FutureTask实现
FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() {
public String call() {
return doSomething();
}
});
executor.execute(futureTask);
2 FutureTask
FutureTask
是Future
接口的典型实现类。FutureTask
是一个可取消的异步计算。其类图结构如下所示:
FutureTask
任务的异步执行,可以使用如下2种方式执行:
- 利用Executor#execute或 ExecutorService#submit()方式执行
- 直接放到Thread线程里执行
FutureTask<String> futureTask = new FutureTask<>(() -> "hello"); Thread t = new Thread(futureTask); t.start(); System.out.println(futureTask.get());
因为FutureTask
实现了Runnable
接口,因此该类天然支持异步线程执行。所有的异步执行,都是通过FutureTask#run()
方法实现,run()
方法执行完毕后,将结果缓存,通过调用FutureTask#get()
方法获取执行的结果。
2.1 执行流程
FutureTask
类的state
字段用于控制异步任务的执行流,如下:
/* state字段的状态流转
NEW -> COMPLETING -> NORMAL
NEW -> COMPLETING -> EXCEPTION
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 Object outcome; // 代表执行结果
private volatile Thread runner; // 正在执行的任务的线程
private Callable<V> callable; // 需要执行的任务
目前state
字段的数值大小有顺序规律:
- 0~2为正常状态
- 3为异常状态
- 4为取消
- 5~6为中断
注意:取消和中断,都可以认为是取消情况。可以直接跳到cancel()方法,该方法会根据入参情况,来决定最终的状态为4或6。即cancel()会导致状态为CANCELLED or INTERRUPTED。
根据字段以及构造方法可知,FutureTask
是一个异步执行框架,而其内部封装了需要执行的任务callable
。异步执行由FutureTask
本身作为Runnable
的实现类完成,在异步的Runnable#run()
中会同步的调用Callable#run()
方法。
2.2 等待节点
FutureTask
是异步执行任务的框架,因此必然存在这种情况:当任务尚在执行过程中,其他线程尝试过去该任务的执行结果。在FutureTask
中会将这些线程打包成WaitNode
节点,等待执行结果:
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
即所有获取结果的线程组成单向链表。在任务执行完成后,将会将这些线程unpark()。
2.3 构造方法
由构造方法可以得知,那些"种类"的任务可以构造出FutureTask
:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW;
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW;
}
由构造方法可知,FutureTask
的初始状态为NEW。
2.4 执行任务
FutureTask
的核心在于任务的异步执行,而异步执行的核心在于Runnable#run()
方法。该方法注意如下几点:
- 因为可能出现异常情况,因此outcome是Object类
- 在执行完任务只会,首先将
state
状态设置为COMPLETING的瞬时态 - 最后,执行
finishCompletion()
进行完成处理
public void run() {
// 判断task的状态流转,并使用CAS方式以runner来记录当前线程
if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex); // 任务执行过程中出现异常
}
if (ran) { // 若任务正常执行结束
set(result);
}
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
// 任务正常结束
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion(); // 任务正常执行完成,则upark()所有等待获取结果的线程
}
}
// 任务异常终止
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
在执行完毕后finishCompletion()
方法将会unpark()
所有获取结果的线程:
// 完成处理的核心在于:
// 1. 循环移除所有的WaitNode节点,并LockSupport.unpark(t)该节点上记录的线程
// 2. 调用done()方法,该方法是预留方法,用于实现类扩展。具体可以参考ExecutorCompeletionService类
private void finishCompletion() {
// assert state > COMPLETING; 即此方法state 一定大于 COMPLETING,即任务已经是完成
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) { // 移除并且unpark()所有等待的线程
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done();
callable = null; // to reduce footprint
}
2.5 获取结果
get()方法由重载方法,其核心在于:若计算尚未完成,则进行等待:创建WaitNode节点进入等待队列中。若最后完成,则调用report()方法获取结果:
- state == NORMAL正常结束,返回结果;
- state >= CANCELLED,取消结束,抛出CancellationException;
- state 为其余任何情况,抛出执行异常
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
if (unit == null)
throw new NullPointerException();
int s = state;
if (s <= COMPLETING && (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
throw new TimeoutException();
return report(s);
}
//// report()方法比较简单,先看此方法
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
所以获取结果的方法核心在于awaitDone()
方法
/// 3种结果:完成、中断、超时
private int awaitDone(boolean timed, long nanos) throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) { // 自旋操作,只有被中断抛出异常,或者state>COMPLETING时,才会跳出方法
if (Thread.interrupted()) { // 清空等待队列,然后抛出异常以响应等待中的线程
removeWaiter(q); // 该方法只会清空等待队列
throw new InterruptedException();
}
int s = state;
if (s > COMPLETING) { // 表明任务已经结束,返回数据
if (q != null) // 如果已经创建了WaitNode节点,则重新将其清空(等待队列中的数据,会在任务完成后的finishCompletion()方法中清空)
q.thread = null;
return s;
} else if (s == COMPLETING) { // COMPLETING表明任务即将完成,无需park(),只需要yield()然后继续尝试
Thread.yield();
} else if (q == null) { // 如果任务一直处于NEW状态,则第1次循环执行到这里,为q创建一个WaitNode()对象
q = new WaitNode();
} else if (!queued) { // 如果任务一直处于NEW状态,则第2次循环执行到这里,将q塞入等待队列中
queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q);
} else if (timed) { // 如果任务一直处于NEW状态,则第3次循环执行到此处或最后的else,将当前线程parkNanos或park()
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
} else {
LockSupport.park(this);
}
}
}
2.6 取消任务
任务在执行期间可以被取消或被中断。取消方法会将状态从:
- 若被取消则:NEW -> CANCELLED
- 若被中断则:NEW -> INTERRUPTING -> INTERRUPTED。
中断or取消,取决于cancel()的入参mayInterruptIfRunning,为true则中断;否则将取消。
注意,状态由 INTERRUPTING -> INTERRUPTED之间,存在时间差,先设置为INTERRUPTING状态后,将线程进行中断,然后设置为INTERRUPTED。由于时间差较短,因此常用Thread.yield()方法让出线程进行等待
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW & UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try {
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
// 取消后,需要unpark()所有在等待的线程
finishCompletion();
}
return true;
}