FutureTask
FutureTask 源码分析
FutureTask 是”一次异步计算”的结果容器:把 Callable 包装成可以丢给线程执行的任务,调用方随时 get() 阻塞取结果、cancel() 取消。
它不用锁,核心是一个 volatile int state 状态机——CAS 保证结果只发布一次,Treiber 栈(无锁链栈)挂起等结果的线程。
线程池 submit() 返回的 Future,默认实现就是它。
// 基于本地 JDK 源码仓 (java.base, JDK 19+ 版本, 含 MhUtil), java.util.concurrent.FutureTask
public class FutureTask<V> implements RunnableFuture<V> { // RunnableFuture = Runnable + Future:既能被 execute,又能被 get
// 七态状态机。合法迁移只有四条路径,> COMPLETING 即尘埃落定:
// NEW -> COMPLETING -> NORMAL 正常出结果
// NEW -> COMPLETING -> EXCEPTIONAL 任务抛了异常
// NEW -> CANCELLED cancel(false)
// NEW -> INTERRUPTING -> INTERRUPTED cancel(true)
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1; // 瞬时态:outcome 正在写入的窗口期
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5; // 瞬时态:cancel(true) 正在发中断
private static final int INTERRUPTED = 6;
private Callable<V> callable; // 任务结束后置 null,减小内存占用
private Object outcome; // non-volatile, protected by state reads/writes 结果或异常,故意不加 volatile(见串讲)
private volatile Thread runner; // 正在执行任务的线程,run() 里 CAS 占坑
private volatile WaitNode waiters; // 等 get() 的线程组成的 Treiber 栈(头插无锁栈)
// 终态之后把 outcome 翻译成三种结局:返回值 / CancellationException / ExecutionException
@SuppressWarnings("unchecked")
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED) // 状态值是按序精心排的:>= CANCELLED 的三个态都算"被取消"
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable volatile 写压轴,顺带把 callable 安全发布出去
}
public boolean isCancelled() {
return state >= CANCELLED;
}
public boolean isDone() {
return state != NEW; // 注意 COMPLETING 也算 done——所以 awaitDone 里见到 COMPLETING 不许空手返回
}
// 取消:只有还停在 NEW 的任务才有机会。mayInterruptIfRunning 决定走 CANCELLED 还是 INTERRUPTING 路线
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW && STATE.compareAndSet
(this, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false; // 已经在完成/取消路上,取消失败
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt(); // 只是"请求"停止,call() 不响应中断就停不下来
} finally { // final state
STATE.setRelease(this, INTERRUPTED);
}
}
} finally {
finishCompletion(); // 不管哪条路线,都要唤醒所有等 get 的线程
}
return true;
}
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING) // NEW 或 COMPLETING 都还没法读 outcome,去等
s = awaitDone(false, 0L);
return report(s);
}
// ... 省略 get(timeout, unit) / resultNow / exceptionNow / state() / done()
// run() 成功后调用:发布结果。CAS NEW->COMPLETING 是进入完成流程的唯一门票
protected void set(V v) {
if (STATE.compareAndSet(this, NEW, COMPLETING)) {
outcome = v; // 普通写。此刻 state=COMPLETING,没有任何读方会碰 outcome
STATE.setRelease(this, NORMAL); // final state release 写:保证 outcome 先于 state 对外可见
finishCompletion();
}
}
// 任务抛异常时的发布路径,结构与 set 完全对称,outcome 存的是 Throwable
protected void setException(Throwable t) {
if (STATE.compareAndSet(this, NEW, COMPLETING)) {
outcome = t;
STATE.setRelease(this, EXCEPTIONAL); // final state
finishCompletion();
}
}
public void run() {
if (state != NEW ||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
return; // CAS runner 占坑失败 = 已有线程在跑,防止同一任务被并发执行
try {
Callable<V> c = callable;
if (c != null && state == NEW) { // 再查一次 state:占坑期间可能已被 cancel
V result;
boolean ran;
try {
result = c.call(); // 真正干活的地方
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex); // 异常不往外抛,存进 outcome——线程池里任务异常"无声"的根源
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null; // 必须等 state 定局才清,否则空窗期另一线程能 CAS 占坑重跑
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
// 确保 cancel(true) 的中断只落在本任务执行期间,不泄漏给 worker 线程的下一个任务
private void handlePossibleCancellationInterrupt(int s) {
// It is possible for our interrupter to stall before getting a
// chance to interrupt us. Let's spin-wait patiently.
if (s == INTERRUPTING)
while (state == INTERRUPTING)
Thread.yield(); // wait out pending interrupt 自旋等 cancel 线程把中断发完
// ... 省略注释:不清中断标志,因为无法区分"取消中断"和"业务自己用的中断"
}
}// 基于本地 JDK 源码仓 (java.base, JDK 19+ 版本), java.util.concurrent.FutureTask —— 等待与唤醒机制
// Treiber 栈节点:比 AQS 节点简单得多,只记线程 + next
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
// 终态后收尾:整棵摘下等待栈逐个唤醒 -> done() 钩子 -> 扔掉 callable
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (WAITERS.weakCompareAndSet(this, q, null)) { // 一次 CAS 摘走整个栈,后来者见 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;
}
}
done(); // 模板方法,ExecutorCompletionService 就是覆盖它把完成任务塞进队列
callable = null; // to reduce footprint
}
// get 的等待主体:无锁自旋状态机,每圈根据观察到的 state 推进一步
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
// ... 省略原注释:精确控制 nanoTime 调用次数与溢出边界
long startTime = 0L; // Special value 0L means not yet parked
WaitNode q = null;
boolean queued = false;
for (;;) {
int s = state;
if (s > COMPLETING) { // 尘埃落定,返回终态
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING)
// We may have already promised (via isDone) that we are done
// so never return empty-handed or throw InterruptedException
Thread.yield(); // 只差最后一笔 outcome,自旋让步比 park 便宜;且 isDone 已承诺完成,不能抛中断
else if (Thread.interrupted()) {
removeWaiter(q); // 从栈里摘掉自己的节点再抛,避免留垃圾
throw new InterruptedException();
}
else if (q == null) { // 第一圈:先建节点,还不入栈
if (timed && nanos <= 0L)
return s;
q = new WaitNode();
}
else if (!queued) // 第二圈:Treiber 头插——先挂 next 再 CAS 头指针
queued = WAITERS.weakCompareAndSet(this, q.next = waiters, q);
else if (timed) {
final long parkNanos;
if (startTime == 0L) { // first time
startTime = System.nanoTime();
if (startTime == 0L)
startTime = 1L;
parkNanos = nanos;
} else {
long elapsed = System.nanoTime() - startTime;
if (elapsed >= nanos) {
removeWaiter(q);
return state; // 超时返回当前 state,由调用方判定抛 TimeoutException
}
parkNanos = nanos - elapsed;
}
// nanoTime may be slow; recheck before parking
if (state < COMPLETING)
LockSupport.parkNanos(this, parkNanos);
}
else
LockSupport.park(this); // 第三圈起:真正挂起,等 finishCompletion 的 unpark
}
}
// ... 省略 removeWaiter:无锁遍历摘除失效节点,撞上竞争就整条重扫原理串讲
从线程池视角走一遍完整链路。pool.submit(callable) 在 AbstractExecutorService 里干两件事:newTaskFor(callable) 把 Callable 包成 FutureTask,然后 execute(ftask) 丢进池子——submit 返回的 Future 和 worker 线程要跑的 Runnable 是同一个对象,这就是 FutureTask 实现 RunnableFuture 的意义。
worker 线程调 run(),先 CAS 把 runner 从 null 换成自己:这个占坑保证同一个任务不会被两个线程并发执行(Treiber 栈和状态机都只按”单一 runner”设计)。
然后 c.call() 真正执行,成功走 set(result),抛异常走 setException(ex)。
发布结果是三步曲:CAS(NEW→COMPLETING)、普通写 outcome、setRelease 终态。
为什么要 COMPLETING 这个中间态?因为 outcome 不是 volatile,写它和写终态 state 是两个动作,必须保证任何读方在看到终态之前绝不去读 outcome——COMPLETING 就是”结果正在写、谁都别碰”的隔离窗口,get 端见到它只会 yield 自旋。
代码块收起展开
为什么 outcome 不加 volatile 也安全?靠 state 建立 happens-before:写方 `setRelease(NORMAL)` 是 release 写,保证之前的 `outcome = v` 先行发布;读方 `report()` 之前必然读过 volatile state(acquire 语义)并看到了终态,于是 outcome 的写对它可见。
这条链省掉了 outcome 上的 volatile 开销;而终态用 setRelease 而非完整 volatile 写,是因为终态值唯一且不再变更,不需要 StoreLoad 全屏障。get 端:get() 读到 state <= COMPLETING 就进 awaitDone,那个 for(;;) 每圈只推进一步——建节点、CAS 头插入 waiters 栈、park——把”检查状态”和”入队挂起”拆成多圈重试,是为了每一步之后都能重新观察 state,任何时刻任务完成都能立刻返回而不是白白挂起。
任务定局后 finishCompletion 用一次 CAS 把整个栈摘下来逐个 unpark,被唤醒的线程回到循环顶部读到终态返回,最后 report(s) 按 NORMAL/EXCEPTIONAL/CANCELLED 三分天下。
cancel 端和 run 端在 NEW 上赛跑:cancel 的 CAS 和 set/setException 的 CAS 抢的是同一个 NEW,谁赢谁定终局。
cancel(false) 只是把状态打成 CANCELLED——正在跑的 call() 会继续跑完,只是 set 里的 CAS 必然失败、结果作废。
cancel(true) 拆成 INTERRUPTING→interrupt→INTERRUPTED 两段,为什么不一步到位?因为 run() 的 finally 需要一个可观察的信号:runner = null 之后重读 state,发现 >= INTERRUPTING 就调 handlePossibleCancellationInterrupt 自旋等到 INTERRUPTED——即等 cancel 线程把 interrupt 发完再让 run() 返回。
否则中断可能在 run() 返回之后才送达,落在 worker 线程的下一个任务头上(leaked interrupt)。
类注释里的 revision notes 也点明了老版本基于 AQS、正是因为取消竞争下的中断残留问题才改成现在的 state + Treiber 栈方案。
设计取舍
- outcome 不用 volatile:靠 state 的 release 写 / volatile 读配对建立 happens-before,COMPLETING 隔离写入窗口,是这个类最精妙的设计。
- cancel(true) 不保证任务停下:interrupt 只是请求,call() 里不检查中断、不碰可中断阻塞,任务照跑;cancel(false) 更是只作废结果。
- cancel 成功后 get() 永远抛 CancellationException——哪怕 call() 其实跑完了并算出了结果,结果也被丢弃。
- submit 会”吞异常”:异常被 setException 存进 outcome,不调 get() 就永远看不到;对比 execute(Runnable) 会走 UncaughtExceptionHandler。
- 等待结构用 Treiber 栈而非 AQS:场景只有”一次性完成、全体唤醒”,无需公平性和独占/共享语义,栈的无锁头插最简单,还绕开了 AQS 方案的中断残留问题。