futuretask 用 locksupport.park/unpark 实现无锁等待唤醒,依托许可机制(避免唤醒丢失)、waitnode 无锁链表(cas 插入+volatile 可见性)和 state 状态机(精准驱动 finishcompletion 唤醒),相比 wait/notify 和自旋更高效安全。

FutureTask 底层用 LockSupport.park() 和 LockSupport.unpark() 实现线程等待与唤醒,核心在于“许可(permit)+ 无锁链表 + 状态驱动”三者协同,不依赖 synchronized,也不怕唤醒丢失。
许可机制保障唤醒不丢失
每个线程内部持有一个隐式许可(初始为 0):
-
unpark(t)将目标线程的许可设为 1,无论该线程是否已调用park() -
park()先检查许可:有则直接返回、不挂起;无则阻塞,直到被 unpark 或中断 - 多次
unpark()不会叠加许可,最多只保留 1 个 —— 这避免了信号堆积,也简化了状态管理
这一特性让 FutureTask 在 set() 时遍历等待链表并逐个 unpark(),完全不用关心 get() 线程是否已开始 park,彻底规避 notify 丢失问题。
WaitNode 链表实现无锁等待队列
FutureTask 维护一个 volatile 的单向链表 waiters,节点类型为 WaitNode:
- 新线程调用
get()发现任务未完成(state == NEW),就用 CAS 将自己头插进链表 - 插入成功后立即调用
LockSupport.park(this)挂起自身 - 整个过程无锁,仅靠
volatile读写 +CAS保证可见性与原子性
这种设计支持高并发 get 调用,多个线程可安全排队,且不引入锁竞争开销。
状态机驱动唤醒时机
FutureTask 的生命周期由 volatile int state 控制(如 NEW → COMPLETING → NORMAL/EXCEPTIONAL):
-
get()中只在state == NEW时 park;一旦状态变更,说明结果已就绪或出错,直接返回 -
set()或setException()完成结果写入后,调用finishCompletion() -
finishCompletion()遍历waiters链表,对每个节点中的线程执行LockSupport.unpark(node.thread)
唤醒动作严格绑定任务完成这一确定事件,不是轮询也不是超时猜测,精准、及时、低延迟。
为什么不用 wait/notify 或自旋
对比其他方式,park/unpark 更契合 FutureTask 的定位:
- 不用 wait/notify:避免 synchronized 块带来的锁开销和死锁风险;notify 随机唤醒,无法确保唤醒正在 get() 的那个线程
- 不用自旋:while(!isDone()) 会空耗 CPU,尤其任务执行时间长时;park 让线程真正休眠,系统资源利用率更高
- 比 Thread.sleep() 更可控:sleep 只能定时,无法被外部精确唤醒;park 可随时被 unpark 中断,响应性更强
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











