一. FutureTask的继承体系
FutureTask 实现了 Runnable 接口和 Future 接口;
FutureTask 也是一个 Runnable 对象;
二. 使用案例
1. 通过Callable构造出FutureTask
通过 callable 对象创建出 FutureTask 对象,实例如下:
java
/**
* 通过 callable 对象创建出 FutureTask 对象
*/
public static void main(String[] args) {
// 创建一个 Callable 对象
Callable<Integer> callable = new Callable<>() {
@Override
public Integer call() throws Exception {
TimeUnit.SECONDS.sleep(2);
int sum = 0;
for (int i = 0; i < 100; i++) {
sum = sum + i;
}
return sum;
}
};
// 使用 Callable 创建 FutureTask
FutureTask<Integer> futureTask = new FutureTask<>(callable);
// 创建一个线程执行 futureTask
Thread thread = new Thread(futureTask);
thread.start();
System.out.println("获取异步任务结果");
Integer result = null;
try {
// 获取异步任务结果,当前主线程会阻塞在这个 get() 中
result = futureTask.get();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("result: " + result);
}
打印如下:
获取异步任务结果
result: 4950
2. 通过Runnable构造出FutureTask
通过 runnable 对象创建出 FutureTask 对象,示例如下:
java
/**
* 通过 runnable 对象创建出 FutureTask 对象
*/
public static void main(String[] args) {
// 创建一个 Runnable 对象
Runnable runnable = new Runnable() {
@SneakyThrows
@Override
public void run() {
TimeUnit.SECONDS.sleep(2);
}
};
// 使用 Callable 创建 FutureTask
FutureTask<Integer> futureTask = new FutureTask<>(runnable, null);
// 创建一个线程执行 futureTask
Thread thread = new Thread(futureTask);
thread.start();
System.out.println("获取异步任务结果");
Integer result = null;
try {
// 获取异步任务结果
result = futureTask.get();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("result: " + result);
}
打印如下:
获取异步任务结果
result: null
3. FutureTask.get()设置超时时间
FutureTask.get()设置超时时间,当指定的超时时间到的时候任务还未执行完成,会抛出 TimeoutException;
java
/**
* FutureTask.get() 设置超时时间
*/
public static void main(String[] args) {
// 创建一个 Callable 对象
Callable<Integer> callable = new Callable<>() {
@Override
public Integer call() throws Exception {
TimeUnit.SECONDS.sleep(2);
int sum = 0;
for (int i = 0; i < 100; i++) {
sum = sum + i;
}
return sum;
}
};
// 使用 Callable 创建 FutureTask
FutureTask<Integer> futureTask = new FutureTask<>(callable);
// 创建一个线程执行 futureTask
Thread thread = new Thread(futureTask);
thread.start();
System.out.println("获取异步任务结果");
Integer result = null;
try {
// 获取异步任务结果,超时时间为 1 秒
// 当 1 秒到的时候任务还未执行完成,会抛出 TimeoutException
result = futureTask.get(1, TimeUnit.SECONDS);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("result: " + result);
}
打印如下:
获取异步任务结果
java.util.concurrent.TimeoutException
at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:204)
at com.zengqiang.futuretask.Test03.main(Test03.java:48)
result: null
三. 源码分析
1. 属性分析
我们看下 FutureTask 的几个重要属性;
java
/**
* 表示当前 task 状态,注意是当前任务的状态,不是线程的状态!
* 任务的状态转换情况如下:
* 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;
// 当前任务执行过程中发生了异常:内部封装的 callable.run() 抛出了异常
private static final int EXCEPTIONAL = 3;
// 当前任务被取消
private static final int CANCELLED = 4;
// 当前任务中断中..
private static final int INTERRUPTING = 5;
// 当前任务已中断
private static final int INTERRUPTED = 6;
// 可以看到 FutureTask 中的任务是以 Callable 的方式存在的
private Callable<V> callable;
// 正常情况下:任务正常执行结束,outcome 保存执行结果,也就是 callable 的返回值
// 非正常情况:callable 抛出了异常,outcome 保存异常对象
private Object outcome;
// 当前执行该任务的线程
private volatile Thread runner;
// 会有很多线程执行 futureTask.get() 去获取当前任务的结果,如果当前任务还未完成,这些线程会阻塞住
// 这个 waiters 其实就是一个链表,将线程包装成 WaitNode 链表并阻塞挂起
private volatile WaitNode waiters;
简单看下 WaitNode 类,可以看到就是维护了一个线程等待节点链表;
java
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
2. 两种构造方法
2.1 传参为Callable
传参为 Callable 对象的情况,直接给 FutureTask 的 callable 赋值,当前任务状态为 NEW;
java
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
// callable 就是我们自己的任务对象,直接设置给 FutureTask 的 callable 属性
this.callable = callable;
// 设置当前任务状态为 NEW
this.state = NEW;
}
2.2 传参为Runnable, result
传参为 Runnable 对象和 result,会将其包装为一个 Callable 对象;
java
public FutureTask(Runnable runnable, V result) {
// 将 Runnable 和 result 转换为 callable 对象
this.callable = Executors.callable(runnable, result);
//设置当前任务状态为 NEW
this.state = NEW;
}
可以看到,FutureTask 把 runnable 和 result 包装为了 Callable 对象;
java
// --------------------------------- Executors -----------------------------------
public static <T> Callable<T> callable(Runnable task, T result) {
// 返回的是 RunnableAdater 适配器类,该类实现了 Callable 接口,聚合了 Runnable 对象
return new RunnableAdapter<T>(task, result);
}
// --------------------------------- RunnableAdapter -----------------------------------
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
// 实际执行的是内部聚合的 Runnable 对象的 run(),并原样返回 result 值
public T call() {
task.run();
return result;
}
}
3. run()
我们知道 FutureTask 也是一个 Runnable 对象,线程执行 futureTask 的时候,入口是它的 run();
FutureTask 的 run() 内部调用的是封装的 callable 的 call();
java
public void run() {
// 1. 如果当前 state != NEW || 当前任务被其他线程抢占执行,直接返回
// 如果当前线程抢占当前 futureTask 成功,futureTask 的 runner 线程为当前线程
if (state != NEW || !RUNNER.compareAndSet(this, null, Thread.currentThread()))
return;
// 此时 task 一定是 NEW 状态,并且当前线程抢占 task 成功,runner 为当前线程
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 2. 执行 callable.call()
result = c.call();
// callable.call() 执行成功,未抛出任何异常,ran 会设置为 true
ran = true;
} catch (Throwable ex) {
// 2.1 callable.call() 抛出了异常,执行 setException(ex)
result = null;
ran = false;
setException(ex);
}
if (ran)
// 2.2 callable.call() 执行成功,执行 set(result)
set(result);
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
3.1 set(result)
set(result) 会将任务执行的结果设置给 outcome,并调用 finishCompletion() 把 get() 中阻塞的线程都唤醒;
java
protected void set(V v) {
// 将任务的状态,即 state 改为 COMPLETING
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
// 更新任务的结果 outcome 为 v
outcome = v;
// 将 result 赋值给 outcome 之后,马上会将当前任务状态修改为 NORMAL 正常结束状态
UNSAFE.putOrderedInt(this, stateOffset, NORMAL);
//把 get() 阻塞挂起的线程唤醒
finishCompletion();
}
}
3.2 setException(ex)
setException(ex) 会将任务执行的异常设置给 outcome,并调用 finishCompletion() 把 get() 中阻塞的线程都唤醒;
java
protected void setException(Throwable t) {
// 将任务的状态,即 state 改为 COMPLETING
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
// 更新任务的结果 outcome 为异常 t
outcome = t;
// 将 t 赋值给 outcome 之后,马上会将当前任务状态修改为 EXCEPTIONAL 异常状态
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL);
//把 get() 阻塞挂起的线程唤醒
finishCompletion();
}
}
4. get()
如果当前任务还未执行完,当前调用 get() 的线程会被阻塞挂起;
直到任务执行完,唤醒这些阻塞线程,并返回任务执行结果;
java
public V get() throws InterruptedException, ExecutionException {
int s = state;
// 如果当前任务是:未执行、正完成,调用 get() 的外部线程会被阻塞在 get() 上
if (s <= COMPLETING)
// awaitDone() 会将外部线程包装为 WaitNode,并阻塞挂起
// 这些线程被唤醒时,awaitDone() 会返回 task 当前状态,这些线程在里面已经睡了一会了
s = awaitDone(false, 0L);
// 根据 state 状态,返回对应的结果 outcome
return report(s);
}
4.1 report(s)
对 state 的状态:
- normal 状态:正常执行状态,返回 Callable.call() 的结果;
- cancelled 状态:被取消状态,抛出 CancellationException 异常;
- exceptional 状态:异常状态,抛出 ExecutionException 异常;
java
private V report(int s) throws ExecutionException {
// 正常情况下:outcome 保存的是 callable 运行结束的结果
// 异常情况下:outcome 保存的是 callable 的 call() 产生的异常
Object x = outcome;
if (s == NORMAL)
// 1. 正常执行状态,返回 Callable.call() 的结果
return (V)x;
if (s >= CANCELLED)
// 2. 被取消状态,抛出 CancellationException 异常
throw new CancellationException();
// 3. 异常状态,抛出 ExecutionException 异常
// ExecutionException 中的原始异常为 Callable.call() 中抛出的异常
throw new ExecutionException((Throwable)x);
}
4.2 awaitDone(false, 0)
将当前线程包装为 WaitNode,作为新的链表头,并挂起阻塞当前线程;
- 将当前线程包装为 WaitNode 入链表作为新链表头,并且将当前节点对应的线程挂起,LockSupport.park();线程进入阻塞状态;
- 线程什么时候会被唤醒呢?当任务被执行完成时,runner 线程会唤醒所有 WaitNode 中等待的线程;
java
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
// 进入自旋
for (;;) {
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
// 1. 如果当前线程是被其它线程 unpark(thread) 唤醒的话
// 获取当前任务最新状态,如果状态 state > COMPLETING,直接返回该 state
int s = state;
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
// 2. 任务状态 state == COMPLETING,表示有其他线程即将执行完任务,当前线程等待一小段时间
else if (s == COMPLETING)
Thread.yield();
// 3. 为当前线程创建 WaitNode
else if (q == null)
q = new WaitNode();
else if (!queued){
// 将当前线程的 WaitNode 入链表中,并且作为链表的头,queued 置为 true
q.next = waiters;
queued = UNSAFE.compareAndSwapObject(this, waitersOffset, waiters, q);
}
else if (timed) {
// 4.1 含等待时间的挂起,执行 LockSupport.parkNanos(this, nanos) 阻塞一段时间
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
// 4.2 不含等待时间的挂起,执行 LockSupport.park(this)
LockSupport.park(this);
}
}
5. get(long timeout, TimeUnit unit)
含阻塞时间的 get(),当指定的超时时间到的时候任务还未执行完成,会抛出 TimeoutException;
java
public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
int s = state;
// 如果当前任务是:未执行、正完成,调用 get() 的外部线程会被阻塞在 get() 上
if (s <= COMPLETING &&
// awaitDone() 会将外部线程包装为 WaitNode,并阻塞挂起
// 这些线程被唤醒时,awaitDone() 会返回 task 当前状态,这些线程在里面已经睡了一会了
// 指定的超时时间到的时候任务还未执行完成的话,抛出 TimeoutException
(s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
throw new TimeoutException();
return report(s);
}