volatile变量通过jvm自动插入内存屏障防止重排序:写操作后加storestore+storeload,读操作前加loadload+loadstore,仅约束关联访存顺序,不保证原子性或全局顺序。

Java 中 volatile 变量防止指令重排序,靠的不是开发者手动插入屏障,而是 JVM 在编译和运行时,自动在 volatile 读写操作的固定位置插入特定组合的内存屏障指令,从而约束编译器、JIT 和 CPU 对相关访存指令的重排行为。
这些屏障不保证全局顺序,也不提供原子性,只对与该 volatile 变量直接关联的读写操作施加局部顺序限制。
volatile 写操作后插入 StoreStore + StoreLoad 屏障
当执行 flag = true;(flag 是 volatile 布尔变量)时,JVM 保证:
- StoreStore 屏障:它前面的所有普通写(如
a = 1; b = 2;)不会被重排到这条 volatile 写之后; - StoreLoad 屏障:它后面的所有读或写操作(如
x = c;或d = 3;)不会被提前到这条 volatile 写之前。
这就形成了一条“写完成分界线”,确保变量初始化完成后才发布状态。例如:data = 42; // 普通写 ready = true; // volatile 写 → 此处插入 StoreStore + StoreLoad
编译器和 CPU 不会把
data = 42挪到ready = true后面,也不会让后续读取data的语句提前执行。
volatile 读操作前插入 LoadLoad + LoadStore 屏障
当执行 if (ready) { ... }(ready 是 volatile 变量)时,JVM 保证:
- LoadLoad 屏障:它前面的普通读(如
r1 = a;)可以重排,但该 volatile 读之后的读操作(如r2 = b;)不能提前到它前面; - LoadStore 屏障:它后面的写操作(如
c = 5;)不会被重排到该 volatile 读之前。
这就形成了一条“读可见分界线”,确保一旦看到ready == true,就能安全读取之前由 volatile 写发布的所有数据。例如:if (ready) { // volatile 读 → 此处插入 LoadLoad + LoadStore result = data; // 这行不会被重排到 if 判断之前 }
跨层级协同生效:从字节码到硬件指令
- 编译器(javac / JIT)生成字节码时,会标记 volatile 字段访问,并在后续汇编生成阶段插入对应屏障;
- JIT 优化(如逃逸分析、字段内联)遇到 volatile 字段会主动禁用可能破坏顺序的变换;
- CPU 硬件层面按架构实现:x86 上常用
lock xchg或mfence,ARM 上则用dmb ish等更严格的指令,统一满足 JMM 规范定义的语义。
它明确不限制的重排类型
- 非 volatile 变量之间的读写顺序,volatile 无法保障;
- 复合操作(如
count++)仍存在竞态,需synchronized或AtomicInteger; - 与 volatile 变量无 happens-before 关系的其他指令,仍可自由重排。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











