futuretask 的 outcome 字段虽未用 volatile 修饰,但通过 state 的 volatile 读写、状态流转及 happens-before 规则保障可见性:set 时先 cas 改 state 再赋值 outcome,最后 putorderedint 更新 state;get 时先检查 state 终态再读 outcome,确保 outcome 对调用线程可见。

FutureTask 的 outcome 字段本身没有用 volatile 修饰,但它在多线程环境下仍能保证结果对调用 get() 的线程可见——靠的不是单个字段的 volatile,而是状态流转 + happens-before 规则的协同保障。
state 变量是可见性链条的锚点
state 是 volatile 修饰的整型字段,它的读写天然具备 happens-before 语义。整个 outcome 的可见性依赖于 state 的两次关键变更:
- 先通过 CAS 将
state从NEW改为COMPLETING(此时 outcome 还未赋值) - 紧接着赋值
outcome = v - 再用
UNSAFE.putOrderedInt将state设为NORMAL(或EXCEPTIONAL)
由于 putOrderedInt 对应的写操作与后续对 state 的读操作之间存在 happens-before 关系,而 outcome = v 出现在该写操作之前,因此它也被“带入”这个顺序中。
get() 方法强制等待完成态
get() 不会直接读 outcome,而是先检查 state:
- 若
state ,说明任务还没真正完成,进入 <code>awaitDone等待 - 只有当
state > COMPLETING(即已变为NORMAL或EXCEPTIONAL)时,才调用report(s)返回 outcome
这就意味着:任何成功返回的 get(),都必然发生在 state 被设为终态之后;而根据 JMM,该写操作之前的 outcome = v 对当前线程一定可见。
outcome 的读写始终受 state 保护
源码注释明确指出:outcome 是 “non-volatile, protected by state reads/writes”。也就是说:
- 写 outcome 只发生在
set()或setException()中,且严格位于 state 变更为终态前 - 读 outcome 只发生在
report()中,且严格位于 state 已确认为终态后 - 中间没有其他线程能绕过 state 判断直接读写 outcome
这种“状态守门”机制,让 outcome 虽无 volatile,却获得了等效的内存屏障效果。
对比 volatile outcome 的代价与收益
如果 outcome 也声明为 volatile:
- 每次读写都要插入内存屏障,影响性能
- 但实际只需要“完成那一刻”的一次强同步,而非全程高频同步
- 当前设计用一次 state 更新 + 一次 outcome 赋值,就完成了结果发布所需的全部同步语义
这是典型的以状态机驱动同步的轻量级实现,既安全又高效。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











