futuretask通过volatile状态变量和cas原子操作确保任务最多执行一次:定义new、completing、normal、exceptional、cancelled五种状态,用cas抢占式变更state实现唯一执行者,volatile outcome保证结果可见性,多次run()因state非new而直接退出。

FutureTask 内部通过volatile 状态变量 + CAS 原子操作协同控制任务生命周期,确保相同实例的 run() 或 get() 触发的计算最多执行一次。
状态机驱动执行控制
FutureTask 定义了五种内部状态(NEW、COMPLETING、NORMAL、EXCEPTIONAL、CANCELLED),其中关键的是:
- NEW:初始状态,表示任务尚未开始执行
- COMPLETING:正在设置结果中(短暂中间态)
- NORMAL / EXCEPTIONAL:已完成,outcome 字段已写入成功结果或异常
所有状态变更都围绕一个 volatile int state 字段进行,保证多线程间状态可见性。
CAS 操作拦截重复执行
当线程调用 run() 或首次 get() 触发执行时,会执行类似如下逻辑:
- 先用
compareAndSetState(NEW, COMPLETING)尝试将状态从 NEW 改为 COMPLETING - 只有第一个成功 CAS 的线程能继续执行 Callable 的
call()方法 - 其他线程 CAS 失败,发现状态已变,直接跳过执行,进入等待结果流程
这种“抢占式状态变更”是防止重复计算的核心机制 —— 不靠锁阻塞,而是靠原子操作快速决出唯一执行者。
结果统一写入与可见性保障
执行成功的线程完成计算后:
- 把结果写入
volatile Object outcome字段 - 再用 CAS 将状态更新为 NORMAL 或 EXCEPTIONAL
后续调用 get() 的线程会检测到非 NEW 状态,直接读取 outcome 并返回。由于 outcome 和 state 都是 volatile 修饰,JMM 保证了结果对所有线程立即可见,无需额外同步。
多次调用 run() 也不重复执行
FutureTask 的 run() 方法开头就判断:if (state != NEW) return;。即使外部多次调用 run()(比如误放入线程池两次),只要状态不是 NEW,就直接退出,不触发任何计算逻辑。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











