volatile已隐式插入内存屏障,无需手动添加;unsafe.fullfence()是显式全序屏障,适用于跨多个非volatile变量的强同步场景,二者不可叠加使用。

Java 中 volatile 本身已隐式插入内存屏障,无需手动添加;Unsafe.fullFence() 是一个显式的、全序的内存屏障,作用强于 volatile 写/读自带的屏障,但二者不能简单“结合使用”来增强语义——它不是叠加关系,而是适用场景不同。
volatile 自带的屏障已足够常见并发场景
编译器和 JVM 在生成字节码时,会为 volatile 字段的读写自动插入对应屏障:
-
volatile 写:插入
StoreStore+StoreLoad屏障,确保之前所有普通写对其他线程可见,且后续读写不被重排到写之前 -
volatile 读:插入
LoadLoad+LoadStore屏障,确保该读能见到之前所有写,并阻止后续写被提前
这些屏障由 JIT 编译器映射为底层 CPU 指令(如 x86 上的 lock addl $0, (%rsp) 或 mfence),已满足 JMM 对可见性与有序性的要求。日常开发中,正确使用 volatile 即可,无需额外干预。
Unsafe.fullFence 的定位是“全栅栏”,非替代 volatile
Unsafe.fullFence() 是 JDK 8+ 提供的原生全内存屏障,效果等价于:
- 禁止该点前后的所有读、写操作相互重排序(即
LoadLoad+LoadStore+StoreLoad+StoreStore) - 强制刷新写缓冲区,使所有先前的写对其他 CPU 立即可见
- 阻塞后续读写直到屏障前所有内存操作完成
它适用于极少数需要**跨多个非 volatile 变量强同步**的场景,例如:
- 手写无锁数据结构时,需保证一组普通字段的写入整体对读线程原子可见(如 RingBuffer 的 cursor 更新)
- 绕过 JMM 抽象、直接对接硬件语义的底层库(如高性能日志框架、自定义内存池)
注意:fullFence() 不作用于某个变量,而是作用于调用位置——它不绑定字段,也不改变字段语义。
不要在 volatile 字段操作后“补加” fullFence
这是常见误解。例如:
❌ 错误示范volatile boolean ready = false; // ... ready = true; // 已含 StoreStore + StoreLoad Unsafe.getUnsafe().fullFence(); // 多余,且可能引入不必要开销
原因:
-
ready = true后的fullFence并不能让ready更“可见”或更“有序”——它的语义已被 volatile 完整覆盖 - 反而增加一次 JNI 调用开销,且在某些平台(如 ARM)可能触发更重的指令(如
dmb ish),降低性能 - 破坏了 JMM 的抽象契约:JVM 本应负责屏障插入,手动混用易导致语义混乱或重复屏障
真正需要 fullFence 的典型模式
当操作的是**普通变量**,且需模拟 volatile 的全局同步效果时才考虑:
int a = 1; int b = 2; // ... 计算逻辑 a = 3; b = 4; Unsafe.getUnsafe().fullFence(); // 确保 a=3 和 b=4 都刷新到主内存,且对其他线程有序可见 // 此后其他线程读 a、b 才能保证看到一致状态
这类写法风险高、可读性差,仅限极少数底层框架;绝大多数业务代码应优先用 volatile、final、synchronized 或 java.util.concurrent 工具类。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











