volatile底层通过插入StoreStore+StoreLoad(写)和LoadLoad+LoadStore(读)内存屏障,保障与该变量关联的读写有序性,但不保证原子性或无关指令的重排序。

volatile 关键字底层通过在字节码中插入特定类型的内存屏障(Memory Barrier),约束编译器、JIT 编译器和 CPU 对相关指令的重排行为,从而保障读写操作的有序性。它不阻止所有重排,只针对与 volatile 变量直接关联的读/写指令施加局部顺序约束。
写操作后插入 StoreStore + StoreLoad 屏障
对 volatile 变量执行写操作时,JVM 会在该写指令之后插入两类屏障:
- StoreStore 屏障:确保该写之前的所有普通写操作,不会被重排到 volatile 写之后;
- StoreLoad 屏障:确保该写之后的所有读/写操作,不会被重排到 volatile 写之前。
这使得 volatile 写成为一道“写完成分界线”。例如:
num = 2; // 普通写
ready = true; // volatile 写 → 此处插入 StoreStore + StoreLoad
编译器或 CPU 不会把 num = 2 挪到 ready = true 后面,也不会让后续其他语句提前执行——这就保证了“初始化完成后再发布状态”的语义。
读操作前插入 LoadLoad + LoadStore 屏障
对 volatile 变量执行读操作时,JVM 会在该读指令之前插入两类屏障:
- LoadLoad 屏障:确保该读之前的所有普通读操作,可以重排,但该读之后的读不能提前;
- LoadStore 屏障:确保该读之后的所有写操作,不会被重排到该读之前。
这使得 volatile 读成为一道“读可见分界线”。例如:
if (ready) { // volatile 读 → 此处插入 LoadLoad + LoadStore
r = num + num; // 这行不会被重排到 if 之前
}
一旦看到 ready == true,就能安全读取 num 的最新值(前提是写端也用 volatile 发布),避免读到未完全初始化的状态。
跨层级协同生效:编译器、JIT、CPU 共同遵守
volatile 的屏障效果不是单一层级的限制,而是贯穿整个执行链路:
- 编译器(javac / JIT):生成字节码时主动避开跨屏障的指令移动;
- JIT 运行时优化:如逃逸分析、字段冗余消除等,在遇到 volatile 字段时自动禁用;
- CPU 硬件层面:x86 上常用
lock addl $0,0(%rsp)或mfence实现;ARM 等弱内存序架构则使用dmb ish等更严格的 barrier 指令。
它明确不禁止的重排类型
volatile 并非万能,以下重排仍可能发生:
- 两个连续的 volatile 写之间可能被重排(如
v1 = true; v2 = false;中的顺序不保证); - 两个连续的 volatile 读之间可能被重排;
- 与 volatile 变量无关的其他普通变量之间的读写重排不受影响;
- volatile 不提供原子性,
i++这类复合操作依然存在竞态。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











