volatile禁止指令重排序是通过内存屏障限制两类关键跨越:volatile写前的读写不能重排到其后,volatile读后读写不能重排到其前;底层由JVM插入StoreStore+StoreLoad(写)和LoadLoad+LoadStore(读)屏障实现。

volatile 的禁止指令重排序,不是靠“锁住所有代码”,而是通过在特定位置插入内存屏障(Memory Barrier),给编译器和 CPU 划出不可跨越的边界。它不改变单线程语义,只约束多线程下与该 volatile 变量有 happens-before 关系的操作顺序。
它禁止的是哪类重排序?
重点不是“所有指令都不能动”,而是限制两类关键跨越:
- volatile 写操作之前的所有读写,不能被重排到该写之后(防止“先写标志,后初始化”变成“先初始化,后写标志”)
- volatile 读操作之后的所有读写,不能被重排到该读之前(防止“读标志后执行业务”,变成“未读标志就提前执行业务”)
比如:obj = new SomeObject(); ready = true;,若 ready 是 volatile,则 obj = ... 一定发生在 ready = true 之前;其他线程读到 ready == true 时,obj 必然已构造完成。
底层靠什么实现?
JVM 在生成指令时,会为 volatile 读写自动插入对应内存屏障:
立即学习“Java免费学习笔记(深入)”;
-
volatile 写:插入
StoreStore(保证前面的写不后移) +StoreLoad(防止后面的读/写越过它提前执行) -
volatile 读:插入
LoadLoad(保证前面的读不后移) +LoadStore(防止后面的写越过它提前执行)
这些屏障会触发 CPU 级别的同步行为(如 x86 上的 mfence 或 lock addl),让缓存一致性协议(如 MESI)介入,从而阻断硬件级重排序。
它管不到哪些地方?
volatile 的有序性作用范围有限,容易误用:
- 只约束与它构成 happens-before 关系的操作,对其他无关变量之间的重排完全不管
- 不能保证复合操作原子性——
counter++即使counter是 volatile,依然可能丢失更新 - 不提供临界区保护,多个 volatile 变量之间若无明确顺序依赖,仍可能被重排
例如:a = 1; b = 2; flag = true; 中只有 flag 是 volatile,那 a=1 和 b=2 之间仍可互换,只是它们都不会跑到 flag=true 后面。
典型场景帮你记住它的作用
几个必须用 volatile 来防重排的地方:
-
状态标志位:如
shutdown = true,确保“设标志”前的清理动作已完成 -
双重检查单例:
instance = new Singleton()被重排可能导致其他线程拿到未初始化完成的对象,volatile 阻断构造与赋值的重排 - 安全发布对象:构造完对象后,用 volatile 写发布引用,确保读方看到的是完全初始化的状态


















