volatile 是线程间状态同步的可见性锚点,确保 state 等关键字段的读写建立 happens-before 关系,支撑 cas 原子操作、条件判断可靠性及队列入队决策,是 jvm 内存模型的基石。

volatile 在 JDK 源码里不是“加个关键字就完事”,它直接定义了线程间状态同步的边界。读 FutureTask 或 AQS 时,看到 volatile int state,别只当它是“防止缓存”,要立刻意识到:这是整个同步逻辑的可见性锚点——所有关键状态跃迁(比如任务从 NEW 到 NORMAL)都依赖它来建立 happens-before 关系。
volatile 是状态判断的唯一可信依据
FutureTask 的 get() 方法在阻塞前会反复检查 state 是否 ≥ COMPLETING;AQS 的 tryAcquire() 也靠 getState() 判断锁是否空闲。这些判断之所以可靠,正是因为 state 是 volatile 的:一个线程写入新状态后,其他线程读到的一定是最新值,不会因 CPU 缓存不一致而误判。
- state 不是“可有可无的修饰”,而是所有分支逻辑(如 if (state == NEW))的前提依据
- 没有 volatile,CAS 操作(如 compareAndSetState)即使成功,后续读取也可能看不到更新结果
- AQS 中的 head/tail 也是 volatile,确保队列结构变更对所有线程可见,否则唤醒可能找不到正确后继节点
volatile + CAS 构成原子状态跃迁的最小契约
单独 volatile 不能保证复合操作原子性,但和 CAS 配合就形成强语义:先读 volatile 值作条件判断,再用 CAS 尝试更新。FutureTask 的 run() 开头就是典型:
- if (state != NEW || !RUNNER.compareAndSet(this, null, Thread.currentThread())) return;
- 这里 volatile 读保证看到真实初始状态,CAS 保证 runner 赋值与状态检查不可分割
- 如果 state 不是 volatile,可能读到旧值导致重复执行;如果不用 CAS,多线程同时进入 run() 会破坏单次执行语义
volatile 字段常与 VarHandle 或 Unsafe 协同演进
JDK 9 后 FutureTask 把 state 管理从 Unsafe 迁移到 VarHandle,但底层仍依赖 volatile 语义:
- VarHandle 的 getVolatile / setVolatile 方法本质是对 volatile 字段的标准化封装
- VALUE.getVolatile(this) 和直接读 volatile state 效果一致,只是更类型安全、可反射控制
- 即使 JDK 17 移除 Unsafe,volatile 仍是 JVM 内存模型强制保障的基石——没有它,VarHandle 的原子性就失去根基
volatile 不负责“排队”,但决定“谁该排队”
AQS 的核心职责是管理等待队列,而 volatile state 决定是否需要排队:
- tryAcquire() 返回 false → 表明资源不可用 → 当前线程必须入队等待
- 这个“不可用”判断来自 volatile state 的读取,不是本地变量或临时计算
- 若 state 非 volatile,不同线程对锁状态认知不一致,可能导致本该排队的线程跳过入队,引发数据竞争
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











