volatile通过插入内存屏障阻止编译器和处理器对指令重排序:写操作后加StoreStore/StoreLoad屏障,读操作前加LoadLoad/LoadStore屏障,确保相关读写不越界,从而保障多线程下执行顺序与预期一致。

volatile 通过插入内存屏障(Memory Barrier)来阻止编译器和处理器对 volatile 变量前后指令的重排序,从而在多线程场景下保障代码逻辑的执行顺序符合预期。
volatile 怎么干预编译器重排序
Java 编译器(如 javac)本身不直接做运行时重排序,但 JIT 编译器(如 HotSpot 的 C1/C2)会在字节码优化阶段,基于 as-if-serial 原则对无数据依赖的指令调整顺序。volatile 的作用不是禁止所有重排,而是为特定位置“设防”:
- 对 volatile 变量的写操作后,插入**写屏障(StoreStore / StoreLoad)**,确保该写之前的所有普通读写不会被移到它之后
- 对 volatile 变量的读操作前,插入**读屏障(LoadLoad / LoadStore)**,确保该读之后的所有普通读写不会被移到它之前
- 这些屏障会告诉 JIT 编译器:“此处不能跨越指令边界重排”,从而固化关键语义顺序
典型场景:初始化与标志位顺序
比如对象初始化后设置完成标志:
private Instance instance; private boolean inited = false; // 线程A instance = new Instance(); // ① 分配内存、构造、赋值引用 inited = true; // ② volatile 写
若 inited 不加 volatile,JIT 可能将②提前到①之前(只要不破坏单线程结果),导致其他线程看到 inited == true,但 instance 仍为 null 或未完全构造。加上 volatile 后,写屏障强制①必须在②之前完成,杜绝这种危险重排。
立即学习“Java免费学习笔记(深入)”;
注意:volatile 不保证复合操作原子性
volatile 只约束变量本身的读/写指令顺序,并不扩展到其周围的业务逻辑。例如:
- 安全:volatile boolean flag = false; flag = true; → 写操作本身有序且可见
- 不安全:if (flag) { count++; } → flag 读是 volatile,但 count++ 是非原子三步操作,仍需额外同步
也就是说,volatile 解决的是“指令顺序失控”问题,不是“竞态条件”问题。
底层靠内存屏障实现,而非魔法
volatile 的有序性不是语言层面的约定,而是 JVM 对不同平台(x86/ARM)生成对应汇编指令的结果。例如在 x86 上,volatile 写会插入 lock addl $0,0(%rsp) 这类带锁前缀的空操作,既刷新写缓冲区,又形成编译器无法跨越的屏障点;在 ARM 上则使用 dmb 指令。这些硬件级指令才是真正“钉住”顺序的物理基础。


















