volatile 关键字通过内存屏障和编译器约束禁止指令重排序:编译器层面禁止跨越 volatile 读写的普通操作重排,处理器层面插入 StoreStore/StoreLoad(写)和 LoadLoad/LoadStore(读)屏障,但仅限于与该 volatile 变量存在 happens-before 关系的操作,不保证复合操作原子性。

volatile 关键字通过“内存屏障(Memory Barrier)”和编译器指令约束,从两个层面禁止指令重排序:对编译器,它禁止在 volatile 读写前后做跨越该变量的重排序;对处理器,它插入特定类型的内存屏障,阻止硬件层面的乱序执行。
编译器层面:禁止重排序优化
Java 编译器(包括 JIT)在生成字节码或机器码时,会遵守 JMM(Java 内存模型)的重排序规则。当一个变量被声明为 volatile:
- 编译器不会把 volatile 写操作之前的普通读写,重排到该写操作之后
- 也不会把 volatile 读操作之后的普通读写,重排到该读操作之前
- 但 volatile 变量自身的读写之间、或与其他 volatile 变量之间的顺序,仍需靠 happens-before 规则保障
处理器层面:插入内存屏障
JVM 在生成汇编指令时,会在 volatile 变量的读/写处插入对应内存屏障:
- volatile 写操作后插入 StoreStore 和 StoreLoad 屏障:确保前面所有写操作先完成,且后续读写不被提前
- volatile 读操作前插入 LoadLoad 和 LoadStore 屏障:确保前面所有读操作已完成,且后续写操作不被提前
- 这些屏障会限制 CPU 流水线和缓存一致性协议的行为,从而阻止硬件级重排序
关键限制:只约束 volatile 周边,不保证整体原子性
volatile 的禁止重排序作用是有范围的:
立即学习“Java免费学习笔记(深入)”;
- 它只约束与该 volatile 变量存在 happens-before 关系的操作,不能防止其他非 volatile 变量间的重排
- 比如
a = 1; flag = true;中 flag 是 volatile,那么 a=1 不会被重排到 flag=true 之后;但若 a 和 flag 都不是 volatile,就无法保证顺序 - 它不提供原子性——像
count++这类复合操作仍需 synchronized 或 AtomicInteger
典型场景:状态标志 + 数据初始化
最常见的应用是“先设标志,再写数据”的安全发布模式:
- 线程 A:设置
data = 42; ready = true;(ready 是 volatile)→ 确保 data 写入一定发生在 ready=true 之前 - 线程 B:检查
if (ready) { use(data); }→ 一旦看到 ready 为 true,就能看到 data 的最新值 - 没有 volatile,编译器或 CPU 可能将 ready=true 提前执行,导致线程 B 读到未初始化的 data


















