volatile 通过建立 happens-before 关系并插入内存屏障间接防止有害重排序,而非直接禁止编译器优化;它约束与自身读写相关的指令顺序,保障可见性但不保证原子性或全局有序。

volatile 关键字本身不直接“禁止”编译器重排序,而是通过在语义层面建立 happens-before 关系,并配合 JVM 在字节码或机器码生成阶段插入特定的内存屏障(Memory Barrier),来约束编译器和处理器的行为,从而间接防止影响正确性的重排序。
编译器重排序的限制前提
编译器优化重排序遵循 as-if-serial 语义:只要单线程执行结果不变,就可以调整指令顺序。例如:
-
int a = 1; int b = 2;→ 可能被重排为先赋值b再赋值a,因为无数据依赖; -
int a = 1; int c = a + 1;→ 不会把c = a + 1提前到a = 1之前,因存在数据依赖。
但多线程下,这种“无依赖就可重排”的逻辑会破坏线程间协作——volatile 就是用来打破这种危险自由度的机制。
volatile 如何干预编译器重排序
JVM 在编译阶段(javac 生成字节码后,JIT 编译为本地代码时)对 volatile 字段的读写操作强制插入内存屏障指令,这些屏障会向编译器发出明确约束信号:
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 对 volatile 写:插入
StoreStore和StoreLoad屏障
→ 确保该写操作前的所有普通写操作,不会被重排到它之后;
→ 同时阻止后续读操作被提前到该写之前; - 对 volatile 读:插入
LoadLoad和LoadStore屏障
→ 确保该读操作前的所有普通读操作,不会被重排到它之后;
→ 同时阻止后续写操作被提前到该读之前。
这些屏障不是 Java 语法,而是 JVM 向底层(汇编或 CPU 指令集)注入的同步语义标记,编译器看到后必须尊重其边界,不得跨屏障移动指令。
一个典型例子说明效果
考虑这段代码:
private int data = 0; private volatile boolean ready = false; // 线程 A 执行 data = 42; // 普通写 ready = true; // volatile 写 → 插入 StoreStore + StoreLoad
由于 data = 42 happens-before ready = true(程序次序规则),而 ready = true 又 happens-before 其他线程对 ready 的读(volatile 规则),因此当线程 B 读到 ready == true 时,一定能看见 data == 42 —— 这种可见性保障,正是靠编译器不敢把 data = 42 重排到 ready = true 之后来实现的。
注意:它不禁止所有重排序
volatile 不是“全局锁”,它只约束与自身读写相关的操作顺序:
- 它不能防止两个 volatile 变量之间的重排序(除非用同一个变量串起 happens-before 链);
- 它不保证复合操作原子性(如
i++); - 它对非 volatile 变量的读写之间,仍可能被重排(除非被屏障隔开)。
它的作用是精准、轻量、有边界的:让关键状态变更成为线程间通信的可靠锚点。

















