volatile写插入StoreStore+StoreLoad屏障,确保前置普通写完成并刷回主内存、后置读写不重排且触发缓存失效;读插入LoadLoad+LoadStore屏障,禁止后续读写重排到其前,并强制加载最新值。

Java 内存模型(JMM)本身不直接暴露内存屏障指令,而是通过特定关键字和同步机制,在编译期和运行期由 JVM 自动插入读/写屏障,从而禁止指令重排序、保障操作的有序性。核心在于:屏障不是手动写的代码,而是 JVM 根据语义自动注入的底层 CPU 指令。
volatile 如何插入屏障实现有序性
对 volatile 变量的写操作,JVM 会在其后插入StoreStore 屏障(防止该写与后续写重排序);对其读操作,会在其前插入LoadLoad 屏障(防止该读与前面读重排序),并在其后插入LoadStore 屏障(防止该读与后续写重排序)。这组组合确保了“volatile 写” happens-before “volatile 读”的传递性,也阻止了常见重排序场景,比如单例双重检查锁中对象未初始化完成就被其他线程看到的问题。
- 示例:在
instance = new Singleton()中,若instance未声明为 volatile,JVM 可能将对象构造(含字段赋值)与引用赋值重排序,导致其他线程拿到一个半初始化的对象 - 加上 volatile 后,JVM 强制在引用赋值指令前后插入屏障,保证构造完成后再发布引用
synchronized 与 monitor 释放/获取隐含的全屏障
进入 synchronized 块时,JVM 插入LoadLoad + LoadStore(读屏障组合);退出时插入StoreStore + StoreLoad(写+全屏障)。其中 StoreLoad 屏障是唯一能同时禁止读写重排序的全屏障,它确保临界区内的所有写操作对后续线程可见,且不会被重排到锁释放之后。
- 这意味着:synchronized 不仅提供互斥,还天然具备“顺序一致性语义”——所有线程看到的加锁/解锁操作顺序是一致的
- 即使没有 volatile,只要共享变量在 synchronized 块内读写,其修改就具有全局有序性和可见性
final 字段的特殊屏障:构造器结束时的 StoreStore
final 字段在构造器内首次赋值后,JVM 会在构造器末尾插入StoreStore 屏障,确保该 final 字段的写不会被重排到构造器外。配合 JMM 的 final 规则,只要对象正确构造(未发生逸出),其他线程通过合法途径看到该对象时,一定能看到 final 字段的正确值,且不会看到其默认值或中间状态。
立即学习“Java免费学习笔记(深入)”;
- 注意:这个屏障只保护 final 字段本身,不保护对象内部可变状态;若 final 引用的对象自身可变,仍需额外同步
- 这是安全发布不可变对象的底层基础
显式调用 Unsafe(不推荐,仅作理解)
JDK 的 Unsafe 类提供了 loadFence()、storeFence() 和 fullFence() 方法,分别对应读屏障、写屏障和全屏障。它们会翻译成 CPU 特定的内存屏障指令(如 x86 上的 lfence、sfence、mfence)。
- 这些方法极少用于业务代码,主要出现在并发工具类(如
java.util.concurrent包)的底层实现中 - 普通开发者应优先使用 volatile、synchronized 或 java.util.concurrent 工具类,而非手动干预屏障


















