Java不提供LoadLoad/StoreStore关键字,它们是JVM根据synchronized、volatile或Unsafe.loadFence()等语义在汇编层插入的内存屏障,具体实现依赖CPU架构。

Java里没有LoadLoad或StoreStore关键字
Java语言层面上压根不暴露LoadLoad、StoreStore这类内存屏障指令——它们是JVM内部对CPU指令的翻译结果,不是你能直接写的语法。你写synchronized、volatile或者Unsafe.loadFence(),JVM才可能在生成的汇编里插入对应屏障。
常见误解是以为能像C++那样手写std::atomic_thread_fence(memory_order_acquire),但在Java里:
- volatile字段读 → 编译后通常带LoadLoad+LoadStore(取决于平台)
- volatile字段写 → 通常带StoreStore+StoreLoad
- Unsafe.loadFence() → 强制插入LoadLoad+LoadStore(HotSpot x86下实际就是mov %rax, %rax这种空操作,但语义存在)
为什么x86上StoreStore经常被省略
x86 CPU本身有较强的内存顺序保证:写-写(Store-Store)天然有序,不需要额外指令。所以HotSpot在x86上编译volatile写时,StoreStore屏障常被优化掉,只留StoreLoad(用lock addl <p>x86 CPU本身有较强的内存顺序保证:写-写(Store-Store)天然有序,不需要额外指令。所以HotSpot在x86上编译<code>volatile写时,StoreStore屏障常被优化掉,只留StoreLoad(用lock addl $0,(%rsp)之类模拟)。
但这不代表它不重要:
- ARM/AArch64没有这个保障,StoreStore会被真实编译成dmb st指令
- 如果你写JNI或用Unsafe做底层并发控制,跨平台时不能假设StoreStore“不存在”
- JMM规范要求语义,不依赖硬件;JVM必须在弱序平台上补足,在强序平台上可省略——这是实现细节,不是行为承诺
Unsafe.loadFence()和Unsafe.storeFence()怎么选
这两个是Java 9+公开的、最接近底层屏障的API,但用错会白费力气:
-
Unsafe.loadFence():确保该点之前所有读完成,之后的读不重排序到它前面 → 对应LoadLoad+LoadStore -
Unsafe.storeFence():确保该点之前所有写完成,之后的写不重排序到它前面 → 对应StoreStore+StoreLoad - 别用
Unsafe.fullFence()代替前两者——它多加了不必要的LoadLoad/StoreStore组合,在x86上等于多一条mfence,性能损耗明显 - 它们不作用于具体变量,只是插入指令栅栏;想保护某个字段,仍得配合
volatile或CAS语义
看到LoadLoad相关错误,大概率是理解错了JMM边界
你不会在运行时看到LoadLoad barrier missing这种错误信息——JVM不会报这个。真出问题时,现象通常是:
- 多线程读到未初始化的对象字段(比如final字段被重排序看到默认值)
- 双检锁单例中,构造函数执行完但对象引用已泄露给其他线程
- @Contended没生效,伪共享依然存在
这时候该检查的不是“怎么加LoadLoad”,而是:
- 是否漏了volatile修饰状态标志
- 是否在synchronized块外读取了本应在锁内发布的引用
- 是否误以为Thread.start()自动建立happens-before,却忘了后续读操作没同步约束
- JVM参数如-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly才能看到实际生成的屏障指令,光看Java代码猜不到










