内存屏障是JVM或CPU插入的同步指令,用于约束Load/Store顺序以保障可见性与有序性;四种类型为LoadLoad(读读)、StoreStore(写写)、LoadStore(读写)、StoreLoad(写读),分别约束不同操作间的执行顺序。

内存屏障不是语法关键字,而是 JVM 或 CPU 在底层插入的同步指令,用来约束读(Load)和写(Store)操作的执行顺序,从而保障多线程下变量的可见性与有序性。四种基本类型——LoadLoad、StoreStore、LoadStore、StoreLoad——本质是不同组合的“读/写”顺序约束,各自解决特定场景的数据竞争问题。
LoadLoad:确保两次读的先后顺序
LoadLoad 屏障插在两个读操作之间,强制前一个读(如 a = x)必须完成并拿到最新值后,后一个读(如 b = y)才能开始。它不保证写入同步,也不影响写操作顺序,只管“读读依赖”。
- 典型出现位置:volatile 变量连续读之间(如 v 和 u 都是 volatile,int i = v; int j = u;)
- 作用目标:防止后读取到过期缓存值,尤其在弱序架构(如 ARM)上必须显式保障
- 注意:x86 等强序平台可能优化掉该屏障,但 JMM 语义仍要求其逻辑存在
StoreStore:确保两次写的提交顺序
StoreStore 屏障位于两个写操作之间,保证前一个写(如 x = 1)已刷新到主内存、对其他线程可见之后,后一个写(如 y = 2)才可执行。它不干预读操作,也不强制写立即落盘,只约束“写写可见性链”。
- 常见于 volatile 写之后紧跟普通写,或两个 volatile 字段连续赋值(如 v = 1; u = 2;)
- x86 架构天然保证 Store-Store 有序,所以 HotSpot 常省略该屏障;但在 ARM 上会编译为 dmb st 指令
- 若省略,可能出现后写先被其他线程观察到,破坏业务逻辑依赖(例如先更新状态再更新时间戳)
LoadStore 与 StoreLoad:跨读写边界的强约束
LoadStore 确保读操作完成后再执行后续写;StoreLoad 则是最强屏障,要求前面所有写都对全局可见后,后面的读才可开始。后者实际包含前三种效果,代价最高——常需清空写缓冲区(buffer flush),在 x86 上体现为 lock addl $0, (%rsp) 类指令。
- LoadStore:出现在 volatile 读之后的普通写(如 i = v; a = i;),防止写被重排到读之前
- StoreLoad:出现在 volatile 写之后的 volatile 读(如 v = 1; j = u;),是 volatile “写后读” happens-before 的关键支撑
- StoreLoad 不是每平台都原生支持,JVM 会在弱序 CPU 上用 full memory fence 实现,在强序平台上用轻量级模拟
屏障如何协同保障变量一致性
单个屏障只管局部顺序,真正保障变量一致性靠的是组合使用和语义约定。比如 volatile 写会插入 StoreStore + StoreLoad,volatile 读插入 LoadLoad + LoadStore,共同构成“写可见→读最新”的完整链路。它们不保证原子性,也不自动加锁,但为无锁编程(如 CAS、双重检查单例)提供了底层同步基础。
- 可见性靠 StoreLoad 和 LoadLoad 配合:写端刷出、读端重载
- 有序性靠四类屏障分别卡住读读、写写、读写、写读四条边界
- Java 程序员无需手写屏障,但理解它们能帮你读懂 volatile 行为、排查重排序 Bug、合理使用 Unsafe.fence()

















