futuretask 的线程安全依赖状态机、cas 和 volatile 协同实现:用 volatile int state + cas 保证状态原子变更与可见性;volatile runner/waiters 实现执行与等待隔离;outcome 读写通过 happens-before 规则保障安全性;cancel(true) 安全中断但需 callable 主动配合。

FutureTask 的线程安全不是靠“锁住整个对象”实现的,而是通过精细的状态控制与原子操作协同保障的。它不阻塞调用方,却能让多个线程安全地查询结果、取消任务或等待完成——关键在于其底层设计把“状态变更”和“结果可见性”都处理得足够严谨。
状态机 + CAS 保证状态变更的原子性
FutureTask 内部用一个 volatile int state 表示任务生命周期(NEW、COMPLETING、NORMAL、CANCELLED 等共 7 种状态),所有状态转换都基于 CAS(Compare-And-Swap)完成。比如从 NEW → COMPLETING,必须满足当前 state 确实是 NEW 才能更新,失败则重试。这种无锁方式避免了 synchronized 带来的性能开销,也杜绝了状态被意外覆盖的风险。
- state 字段被 volatile 修饰,确保任意线程对它的读写都立即对其他线程可见
- CAS 操作由 Unsafe 类提供,底层依赖 CPU 指令(如 x86 的 cmpxchg),天然线程安全
- 状态只能单向流转(如不能从 NORMAL 回退到 NEW),逻辑上防止非法跃迁
runner 和 waiters 字段协同实现执行与等待的隔离
FutureTask 记录正在执行任务的线程(volatile Thread runner)和等待结果的线程链表(volatile WaitNode waiters)。这两个字段都用 volatile 修饰,配合 CAS 更新,让“谁在跑”和“谁在等”始终可被准确感知。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- run() 方法启动时,先用 CAS 把当前线程设为 runner;如果已被占用,直接返回,避免重复执行
- get() 方法发现任务未完成,就用 CAS 将自己加入 waiters 链表头,并 park 当前线程;任务结束时,finishCompletion() 会 unpark 所有 waiters
- cancel(true) 中中断 runner 线程前,也需确认 runner 非 null 且状态仍为 NEW 或 RUNNING,否则不触发 interrupt()
outcome 字段的写入与读取遵循 happens-before 规则
任务结果(正常值或异常)存于 Object outcome 字段。它本身不加 volatile,但其写入发生在 state 从 COMPLETING 更新为 NORMAL/EXCEPTIONAL 之后,而 get() 方法读 outcome 前必先读 state ——JVM 的 volatile 写读顺序建立了 happens-before 关系,保证 outcome 对读线程一定可见。
- set(V v) 和 setException(Throwable t) 都先写 outcome,再用 CAS 更新 state
- get() 中先检查 state 是否 ≥ NORMAL,再读 outcome,不会看到“半写”结果
- 这种设计比直接 volatile outcome 更轻量,又不失安全性
协作式中断不破坏线程安全,但依赖任务代码配合
cancel(true) 调用 interrupt() 是线程安全的操作,但它只改变目标线程的中断标志位。FutureTask 本身不干涉任务内部逻辑,所以是否真正响应中断、何时退出、如何清理资源,完全由 Callable 实现决定。这不影响 FutureTask 自身状态机的安全性,但决定了“取消”语义能否落地。
- interrupt() 是 JVM 层面的线程级安全操作,不会引发竞态
- 任务若忽略中断(如纯计算循环中不查 isInterrupted()),FutureTask 的状态仍会正确变为 INTERRUPTED,只是 runner 线程继续运行
- 安全取消的关键不在 FutureTask,而在 Callable 是否主动检查中断并优雅退出
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










