volatile关键字通过内存屏障禁止与该变量读写相关的指令重排:写操作后插入StoreBarrier,确保之前操作不被重排到其后;读操作前插入LoadBarrier,确保之后操作不被重排到其前;但不保证原子性,也不约束非volatile变量或连续volatile操作间的重排。

volatile 关键字在底层通过插入内存屏障(Memory Barrier),主要阻止的是编译器和处理器层面的三类指令重排,但不阻止所有重排,而是有针对性地约束与 volatile 变量读写相关的操作顺序。它不保证原子性,也不影响无关指令的重排。
1. 写操作前后的重排被禁止(写屏障 sfence)
对 volatile 变量执行写操作时,JVM 会在该写指令后插入写屏障(Store Barrier),从而禁止两类重排:
- 写屏障之前的普通读/写指令,不能重排到 volatile 写之后;
- 写屏障之后的普通读/写指令,不能重排到 volatile 写之前。
例如:
num = 2; // 普通写 ready = true; // volatile 写 → 此处插入写屏障
编译器或 CPU 不会把 num = 2 挪到 ready = true 后面执行,也不会让后续其他非依赖指令提前到 ready 赋值前——这确保了“状态发布”语义:ready = true 之前的所有初始化操作(如 num = 2)一定已完成并对其它线程可见。
立即学习“Java免费学习笔记(深入)”;
2. 读操作前后的重排被禁止(读屏障 lfence)
对 volatile 变量执行读操作时,JVM 会在该读指令前插入读屏障(Load Barrier),从而禁止:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 读屏障之后的普通读/写指令,不能重排到 volatile 读之前;
- 读屏障之前的普通读/写指令,可以重排到其前面(不禁止)。
例如:
if (ready) { // volatile 读 → 此处插入读屏障
r = num + num; // 这行不会被重排到 if 之前
}
这样能保证一旦看到 ready == true,就一定能读到 num 的最新值(前提是写端也用 volatile 发布),避免“读到半初始化状态”。
3. 禁止编译器、JIT 和 CPU 三级重排协同生效
volatile 的禁止重排效果是跨层级的:
- 编译器重排:Javac 和 JIT 编译器会尊重 volatile 语义,不将相关指令跨屏障移动;
- JIT 优化限制:比如逃逸分析、冗余字段消除等优化,在遇到 volatile 字段时会退避;
-
CPU 级重排:x86 上通常用
lock addl $0,0(%rsp)或mfence实现屏障效果;ARM 等弱序架构则依赖更严格的 barrier 指令(如dmb ish)。
注意:volatile 并不禁止两个 volatile 操作之间的重排(比如连续两次 volatile 写之间仍可能被重排),它只保障单次读/写操作与其屏障的局部有序性。
4. 它不阻止哪些重排?
明确边界有助于避免误用:
- 非 volatile 变量之间的读写重排不受影响;
- volatile 读与 volatile 读之间、volatile 写与 volatile 写之间,没有 happens-before 关系,不构成顺序约束;
- 不会阻止不同线程中无依赖关系的指令重排(即不提供跨线程全局顺序);
- 无法防止复合操作的非原子性,例如
counter++即使修饰为 volatile,仍存在读-改-写竞态。
本质上,volatile 提供的是单变量的读写有序性 + 内存可见性传递链,而非锁级别的全序控制。

















