volatile写插入storestore+storeload屏障,确保前置普通写不重排到其后、后续读不提前;读插入loadload+loadstore屏障,保证前置读完成、后续写不重排到其前。

Java 内存模型(JMM)本身不直接提供内存屏障指令,而是通过 volatile 关键字的语义约定,要求 JVM 在编译和运行时自动插入特定类型的内存屏障,从而禁止相关方向的指令重排序。
volatile 触发的屏障组合是关键
每次对 volatile 变量的读或写,JVM 都会在生成的汇编代码中插入一组硬件级内存屏障,具体如下:
-
volatile 写操作:插入 StoreStore + StoreLoad 屏障
→ StoreStore 确保该写之前的所有普通写(如a = 1)不会被重排到它之后;
→ StoreLoad 阻止该写之后的任意读操作(如int x = b)被提前执行,避免读到未完成的中间状态。 -
volatile 读操作:插入 LoadLoad + LoadStore 屏障
→ LoadLoad 保证该读之前的所有普通读(如int y = c)已完成;
→ LoadStore 防止该读之后的任意写(如d = 2)被重排到它前面,确保读结果对后续写可见。
屏障只约束与 volatile 变量有 happens-before 关系的操作
内存屏障不是全局生效的,它的作用范围严格绑定于 volatile 变量本身,并仅对存在 happens-before 关系的操作起效:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 若写线程执行
a = 1; flag = true;(flag是 volatile),则a = 1happens-beforeflag = true(程序次序规则),而flag = true又 happens-before 其他线程读flag(volatile 变量规则),经传递性,a = 1对读线程可见; - 但两个非 volatile 变量
a和b之间的读写顺序,无法靠 volatile 保障; - 复合操作(如
count++)仍需synchronized或AtomicInteger,因为屏障不提供原子性。
编译器和处理器都受约束
JMM 不仅约束 CPU 执行层面,也约束编译优化行为:
- 编译器不能把 volatile 写之前的普通读写重排到该写之后;
- 也不能把 volatile 读之后的普通读写重排到该读之前;
- HotSpot JIT 还会做智能优化:比如 StoreLoad 屏障开销最大,JIT 仅在后续确实存在普通读操作时才插入,避免无谓性能损耗。
典型场景验证机制有效性
以双重检查锁定(DCL)单例为例:
- 若
instance不是 volatile,JVM 可能将对象构造(含字段初始化)与instance = new Singleton()的引用赋值重排序,导致其他线程拿到半初始化对象; - 加上 volatile 后,StoreLoad 屏障强制保证:对象构造全部完成并刷新到主内存后,
instance引用才被写入,彻底规避该问题。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










