FutureTask通过waiters栈式链表+UNSAFE原子操作+状态机实现高效并发等待,get()时CAS入栈并park,finishCompletion时逆序unpark唤醒,兼顾性能与简洁性。

FutureTask 在大量并发线程调用 get() 时,并不会让所有等待线程“一哄而上”争抢结果,而是通过一套轻量、有序的线程阻塞与唤醒机制来协调。它的底层核心不是锁竞争,而是基于 waiters 链表 + UNSAFE 原子操作 + 状态机驱动 的协作模型。
waiters 链表:无锁栈式等待队列
FutureTask 内部维护一个 volatile 的 waiters 字段,类型是 WaitNode(单向链表节点)。每当线程调用 get() 且任务未完成时,会:
- 构造一个新
WaitNode,把自己(当前线程)存入其thread字段 - 用
UNSAFE.compareAndSetObject将该节点 头插 到waiters链表头部(CAS 操作,无锁) - 调用
LockSupport.park(this)使线程进入 WAITING 状态
这个过程不加锁、不阻塞其他 get 调用,多个线程可并发入队,形成 LIFO(后进先出)等待栈。
状态变更触发唤醒:run() 完成后一次性通知
当任务执行完毕(run() 返回)、异常结束或被取消时,FutureTask 会原子更新内部 state 字段(如从 RUNNING → NORMAL),并紧接着执行 finishCompletion() 方法:
立即学习“Java免费学习笔记(深入)”;
- 将
waiters引用置为null(切断链表) - 遍历原 waiters 链表中所有节点
- 对每个节点中的
thread调用LockSupport.unpark(thread)
注意:唤醒顺序是逆插入顺序(即最后 park 的线程最先被 unpark),但 JVM 不保证 unpark 后线程立即调度——实际执行仍由操作系统线程调度器决定。
get() 阻塞逻辑:自旋 + park 的双重保障
get() 方法中关键逻辑在 awaitDone(boolean timed, long nanos):
- 先短暂自旋(检查 state 是否已变),避免立即 park 的开销
- 若 state 仍未就绪,则入队 + park
- 被 unpark 唤醒后,再次检查 state —— 若仍非终态(如中途被 cancel),则继续 park 或返回异常
这种设计减少了上下文切换次数,也避免了虚假唤醒导致的重复等待。
为什么不是公平唤醒?
FutureTask 没有使用 Condition 或 AQS 的 FIFO 队列,而是选择栈式 waiters,原因很务实:
- 绝大多数场景下,get() 等待者数量少(常为 1~3 个),LIFO 开销更低
- 无需维护复杂队列结构,减少内存分配和 CAS 冲突
- 任务完成是“一次性事件”,所有等待者最终都能拿到结果,无需强公平性
如果真有数百并发 get,瓶颈通常不在唤醒链,而在结果计算本身或共享资源竞争——此时应考虑降级为批量提交、缓存结果或改用 CompletableFuture 的异步编排能力。


















