volatile写操作插入StoreStore和StoreLoad屏障:前者确保之前普通写先完成并刷入主存,后者禁止后续读被重排到volatile写之前;读操作插入LoadLoad和LoadStore屏障,保障读写顺序与跨线程可见性。

volatile 本身不直接“禁止重排序”,而是通过在读写操作处插入特定内存屏障,让编译器和 CPU 尊重这些屏障的边界,从而间接约束指令顺序。
volatile 写操作如何阻止重排序
对 volatile 变量执行写操作时,JVM 会在其前后插入两类内存屏障:
-
StoreStore 屏障:确保该写操作之前的所有普通写(如
a = 1)必须先完成并刷入主内存,不能被重排到 volatile 写之后 - StoreLoad 屏障:这是最重的屏障,它强制 volatile 写完成后,才允许后续的读操作(包括普通变量读)执行,防止后续读被提前
例如:a = 1; flag = true;(flag 是 volatile),由于 StoreStore 屏障的存在,a = 1 一定在 flag = true 之前对其他线程可见;而 StoreLoad 屏障则避免了其他线程在看到 flag == true 后,却读到旧的 a 值。
volatile 读操作如何阻止重排序
对 volatile 变量执行读操作时,JVM 插入:
立即学习“Java免费学习笔记(深入)”;
-
LoadLoad 屏障:保证该读操作之前的所有读(如读取
config)已完成,后续读不能越过它提前执行 -
LoadStore 屏障:保证该读操作完成后,才允许后续的写操作(如
result = compute())发生,防止写被重排到读之前
这使得“先读 volatile 标志,再用它驱动后续逻辑”这一语义得到硬件级保障。比如在双重检查单例中,读 instance != null 后,能安全访问已初始化的字段。
内存屏障如何落地为 CPU 指令
不同平台实现不同,但效果一致:
- x86/x64 上,volatile 写通常对应带
lock前缀的指令(如lock xchg),它会触发缓存一致性协议(MESI),使其他 CPU 失效对应缓存行,并强制刷新本地写缓冲区 - volatile 读在 x86 上虽无显式屏障指令(因 x86 内存模型较强),但 JVM 仍会插入语义等价的屏障,确保从主内存或最新缓存加载值
- 在 ARM 或 RISC-V 等弱序架构上,JVM 会明确插入
dmb(data memory barrier)等指令
关键提醒:屏障只管“相关指令”,不锁整个方法
内存屏障的作用范围是局部的、有方向的:
- 它不阻止屏障前后的任意两条无关指令重排,只约束屏障两侧的读写类型组合(如 Store-Load、Load-Store)
- 它不提供互斥或临界区保护,也不影响非 volatile 变量之间的重排(除非它们与 volatile 操作构成 happens-before 链)
- 它的最终目标不是“让代码按源码顺序执行”,而是确保跨线程观察到的操作顺序满足 JMM 定义的可见性与有序性契约


















