Future 接口介绍
Future 表示一个异步计算,提供了用于检查异步计算是否完成,等待异步计算结果,取消异步计算任务等方法。等待获取异步计算结果会阻塞当前线程,直到超时或者获取到异步计算的结果。如果异步计算已经完成,取消计算任务的方法将不会生效。如果希望利用 Future 来处理异步但是没有计算结果的任务,可以使用 Future<?>,然后返回 null 实现。
Future 的使用例子如下:
- 利用线程池的 submit 方法获取生成 Future,利用 Future 获取异步任务结果
interface ArchiveSearcher { String search(String target); }
class App {
ExecutorService executor = ...
ArchiveSearcher searcher = ...
void showSearch(final String target)
throws InterruptedException {
Future<String> future
= executor.submit(new Callable<String>() {
public String call() {
return searcher.search(target);
}});
displayOtherThings(); // do other things while searching
try {
displayText(future.get()); // use future
} catch (ExecutionException ex) { cleanup(); return; }
}
}
- 自定义 FutureTask,利用线程类执行异步任务,利用 FutureTask 获取异步任务结果
public class FutureTaskTest {
public static void main(String[] args) throws InterruptedException, ExecutionException {
long start = System.currentTimeMillis();
Callable<String> callable = () -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "任务执行结果";
};
FutureTask<String> futureTask = new FutureTask<>(callable);
Thread thread = new Thread(futureTask);
thread.start();
System.out.println(futureTask.get());
}
}
Future 的方法列表:
/**
* 取消异步任务。
* 该方法尝试取消异步任务,但是不一定能够取消成功。如果任务已经完成,获取已经被取消过,或者由于其他原因取消了,那么都会取消失败。
* 如果异步任务在取消的时候还未开始,那么异步任务将不会开始运行;
* 如果任务已经开始运行,那么 mayInterruptIfRunning 参数将决定是否通过中断线程来取消异步任务。
*
* 如果取消成功,后续调用 isDone 和 isCancelled 都将会返回 true。
*
* @param mayInterruptIfRunning 参数决定运行中的任务是否通过中断线程来取消任务。如果设置 false,那么运行中的任务无法取消。
* @return 如果取消成功则返回 true,否则返回 false。
*/
boolean cancel(boolean mayInterruptIfRunning);
/**
* 如果异步任务取消成功,那么返回 true,否则返回 false
*
* @return 如果任务取消成功返回 true,否则返回 false
*/
boolean isCancelled();
/**
* 如果任务执行完毕(包括被取消成功)返回 true,否则返回 false。
*
* 任务正常结束,任务被取消成功,任务执行中发生异常而结束都是任务执行完毕。
* @return 任务执行完毕则返回 true,否则返回 false
*/
boolean isDone();
/**
* 等待获取任务计算结果。该方法或中断当前线程,知道异步任务结束。
*
* @return 任务计算结果
* @throws CancellationException 任务如果取消则抛出该异常
* @throws ExecutionException 任务执行过程中发生异常
* @throws InterruptedException 执行异步任务的线程被中断
*/
V get() throws InterruptedException, ExecutionException;
/**
* 可设置超时时间的等待获取异步任务计算结果。该方法会中断当前线程直到获取异步任务结果或者超时。
*
* @param timeout 超时时间
* @param unit 时间单位
* @return 异步任务结果
* @throws CancellationException 任务被取消
* @throws ExecutionException 异步任务执行过程中发生异常
* @throws InterruptedException 执行异步任务的线程被中断
* @throws TimeoutException 等待时间超过设置的超时时间
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;```
<a id="org58d0423"></a>
# RunnableFuture
Future 本身是一个异步计算结果,为了让 Future 成为一个任务,所以定义了一个 RunnableFuture。RunnableFuture 即继承 Runnable,有继承 Future,即表示是一个异步计算结果,也表示一个可执行的一个任务。
```java
/**
* RunnableFuture 同时表现成一个异步计算结果和可通过线程执行的任务。
*/
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* 如果任务未取消,那么将任务的计算结果交给异步计算结果(Future)
*/
void run();
}
FutureTask
介绍
FutureTask 是 RunnableFuture 的具体实现,也是线程池框架执行异步任务的结算结果。
FutureTask 的类继承关系如下:
原理
FutureTask 有 Callable callable 属性,用于存储外部任务。当初始化 FutureTask 时,需要指定一个具体的外部任务初始化 callable 属性。
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 本身是一个任务,提供 run 方法的具体实现。run 方法会执行 callable.call()拿到任务结果,并将结果赋值给另一个属性 Object outcome。
当使用 get()方法获取异步任务结果时,其实就是等待任务 run 方法执行结束:在异步任务未结束前,生成等待节点,加入到等待队列,线程被挂起。如果异步任务执行结束后会给outcome赋值异步任务计算结果,并唤醒等待队列中的线程。
get()方法通过 FutureTask 的状态判断任务是否执行完成,如果是执行完成的状态,那么返回 outcome,如果线程被中断或者超时或者执行任务过程中抛出异常则 get 方法抛出对应的异常。
FutureTask 的状态:
任务执行成功的状态变化示意图:
代码详情
重要属性
/**
* state 表示 FutureTask 的状态。可能的状态一空有 7 种,初始化时是 NEW。状态变化的路径为:
* 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;
/** 代表一个外部任务。任务执行完毕后会置为 null */
private Callable<V> callable;
/** 存储异步任务执行结果 */
private Object outcome;
/** 用于存储执行异步任务的线程的引用, */
private volatile Thread runner;
/** 单向链表,用来存储调用 get 方法阻塞等待异步任务执行结果的线程 */
private volatile WaitNode waiters;```
<a id="org00f89d1"></a>
### 构造方法
FutureTask 初始化需要外部指定的参数是 callable 对象,需要在执行任务时执行 callable.call 获取异步任务结果。所以构造方法都是围绕初始化 callable 对象而进行
```java
/**
* 根据指定的 callable 对象生成一个 FutureTask 对象。
*
* @param callable callable 对象
* @throws NullPointerException 如果 callable 对象是空对象,那么抛出 NullPointerException 异常
*/
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
// FutureTask 初始化状态是 NEW
this.state = NEW;
}
/**
* 根据指定的 Runnable 对象生成 FutureTask。
* 借助 Executors public static <T> Callable<T> callable(Runnable task, T result)方法,利用 runnable 对象和泛型生成一个 callable 对象。
*
* @param runnable 可执行任务
* @param result callable 的执行结果的类型(执行 callable.call 的返回类型)
* @throws NullPointerException 如果 runnable 是空对象,那么抛出空指针异常
*/
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}```
<a id="org224cb8b"></a>
### Future 接口实现
```java
/**
* 取消异步任务
* @param mayInterruptIfRunning 参数决定运行中的任务是否通过中断线程来取消任务。如果设置 false,那么运行中的任务无法取消。
* @return 如果取消成功则返回 true,否则返回 false。
*/
public boolean cancel(boolean mayInterruptIfRunning) {
// 如果 FutureTask 状态是 NEW,并且通过中断线程来取消任务,那么将状态改成 INTERRUPTING,即 NEW -> INTERRUPTING -> INTERRUPTED 的状态变化
// 如果 FutureTask 状态是 NEW,并且不通过中断线程来取消,那么将状态改成 CANCELLED,即 NEW -> CANCELLED 的状态变化流程
// 如果判断条件不成功,说明此事 FutureTask 已经不是 NEW 的状态,那么直接返回取消失败。
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try {
// 如果是通过中断线程来取消任务,那么将线程置为中断状态,并且将 FutureTask 的状态改成 INTERRUPTED
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
// 唤醒所有等待获取结果的线程
finishCompletion();
}
return true;
}
/**
* FutureTask 的异步任务是否已经被取消
*/
public boolean isCancelled() {
// 如果 FutureTask 的状态是 CANCELLED,INTERRUPTING,INTERRUPTED 的状态,那么 FutureTask 就是被取消的。INTERRUPTING 或者 INTERRUPTED 的状态可以通过 FutureTask.cancel(true)产生。
// EXCEPTIONAL 是在执行异步任务过程中抛出的异常产生并非是取消任务产生。
return state >= CANCELLED;
}
/**
* 返回异步是否已经结束。非开始的状态都是结束的装填。
*/
public boolean isDone() {
// 在 callable.call 方法结束前,FutureTask 的状态都是 NEW 的状态,call 结束后 FutureTask 的状态才会改成其他状态,所以非 NEW 的状态都是结束状态。
return state != NEW;
}
/**
* 获取异步任务的计算结果
*/
public V get() throws InterruptedException, ExecutionException {
int s = state;
// 如果 s <= COMPLETING 说明任务还没执行完成那么等待异步任务完成
if (s <= COMPLETING)
s = awaitDone(false, 0L);
// 如果正常获取到异步任务计算结果,那么返回异步计算任务结果;如果异步任务被取消,那么抛出 CancellationException;如果异步任务执行过程中发生异常,那么抛出 ExecutionException 异常
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);
}
/**
* 唤醒所有等待获取异步任务结果的线程
*/
private void finishCompletion() {
// 如果等待队列中有节点,那么唤醒节点上的线程,并将节点提出等待队列
for (WaitNode q; (q = waiters) != null;) {
// 利用 CAS 比较修改,将等待队列置为 null
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
// 唤醒节点上的线程
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;
}
}
// 桩代码,FutureTask 没有任何功能,留给子类做完成任务后的定制化处理
done();
callable = null; // to reduce footprint
}
/**
* 等待异步任务结束
*
* @param timed 是否设置了超时时间
* @param nanos 超时时间
* @return state FutureTask 结束 awaitDone 时的的状态
*/
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
// 计算等待的毫秒数
final long deadline = timed ? System.nanoTime() + nanos : 0L;
// 等待获取异步任务的节点
WaitNode q = null;
// 是否进入等待队列
boolean queued = false;
for (;;) {
// 此处应该是是线程阻塞(park 方法能够响应线程中断)时,线程被中断。如果线程被中断,那么从队列中删除对应的等待节点,抛出 InterruptedException
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
// FutureTask 当前状态
int s = state;
// 如果不是 NEW 那么没有必要继续等待异步任务执行结果
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
// 如果是 COMPLETING,FutureTask 的异步任务即将完成或者被中断结束,那么将 CPU 让渡出去继续等待最终完成。
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
// 需要线程阻塞等待,先构建等待节点
else if (q == null)
q = new WaitNode();
// 如果等待节点未入队,那么将等待节点添加到队列队首,
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
// 如果设置超时时间,那么先判断是否已经超过了超时时间,如果超过则删除等待节点并退出;否则 LockSupport.parkNanos 阻塞线程。
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
LockSupport.park(this);
}
}
/**
* 从阻塞队列中删除指定的等待节点
*/
private void removeWaiter(WaitNode node) {
if (node != null) {
node.thread = null;
retry:
for (;;) { // restart on removeWaiter race
for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
s = q.next;
if (q.thread != null)
pred = q;
else if (pred != null) {
pred.next = s;
if (pred.thread == null) // check for race
continue retry;
}
else if (!UNSAFE.compareAndSwapObject(this, waitersOffset,
q, s))
continue retry;
}
break;
}
}
}
Runnable 的实现
/**
* 任务的实现细节
*/
public void run() {
// 如果 FutureTask 不是初始化状态或者 runner 已经指向了具体的线程,说明 FutureTask 已经被取消或者已经被执行(run 方法已经被调用过),那么直接退出该方法
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
// c != null && state == NEW 说明任务未被执行并且未被取消
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 执行异步任务并获取任务结果
result = c.call();
ran = true;
// 如果执行异步任务过程中抛出异常,那么捕获并将 FutureTask 的状态置为 COMPLETING,将异常赋值给 outcome,之后将状态置为 EXCEPTIONAL,即 NEW -> COMPLETING -> EXCEPTIONAL 的变化流程。
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
// 如果正常执行结束,将 FutureTask 状态从 NEW 置为 COMPLETING,将结果赋值给 outcome,将状态置为 NORMAL,即 NEW -> COMPLETING -> NORMAL 状态变化流程。
if (ran)
set(result);
}
} finally {
// 清空 runner
runner = null;
int s = state;
// 再次确认是否有执行通过线程中断来取消操作,如果有那么等待取消完成。
// s >= INTERRUPTING 可能产生的情况是:在执行 set 操作前 FutureTask 被取消,导致状态从 NEW 编程变成 INTERRUPTING。那么 run 方法需要等待取消操作结束再结束。这是为了确保运行时的 cancel(true)的操作传递过来的取消操作都能在 run()方法结束前处理。
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
/**
* 如果在执行异步任务过程中捕获到问题,那么将捕获到的异常的原因交给 outcome,并唤醒所有等待获取结果的线程。
*
* @param t 异常的原因
*/
protected void setException(Throwable t) {
// 如果 FutureTask 的状态是 NEW,那么改成 COMPLETING。
// 如果在处理异常前一刻异步任务被取消,那么会走取消流程,因为 CAS 修改只针对 NEW 的状态。
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
// 唤醒等待获取异步任务结果的线程
finishCompletion();
}
}
/**
* 将任务的执行结果赋值给 outcome,并修改 FutureTask 的状态
*
* @param v the value
*/
protected void set(V v) {
// CAS 比较修改 FutureTask 的状态,从 NEW 改成 COMPLETING.
// 如果修改成功则将结果赋值给 outcome
// 再将 FutureTask 的状态改成 NORMAL
// 唤醒等待获取异步任务计算结果的线程
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}