volatile通过插入storestore+storeload(写)和loadload+loadstore(读)内存屏障保证有序性与可见性,但不保证复合操作原子性。

volatile 在多线程中不靠锁,而是靠插入内存屏障(Memory Barrier)来约束指令执行顺序,从而保证读写有序性。它不提供原子性,但能确保变量的可见性和禁止特定方向的重排序。
volatile 写操作插入两类写屏障
当一个线程写 volatile 变量时,JVM 会在字节码层面插入两个关键屏障:
- StoreStore 屏障:位于 volatile 写之前,保证该写操作前的所有普通写(如给其他字段赋值)不会被重排到它之后;
- StoreLoad 屏障:位于 volatile 写之后,防止该写与后续的读/写操作重排序,尤其保障写后其他线程能及时看到变更。
例如:data = 42; flag = true;,若 flag 是 volatile,JMM 保证 data = 42 一定在 flag = true 之前完成并刷入主内存,避免“半初始化”问题。
volatile 读操作插入读屏障
当一个线程读 volatile 变量时,JVM 插入:
- LoadLoad 屏障:确保该读操作前的所有加载(读取)已完成,不会被重排到 volatile 读之后;
- LoadStore 屏障:防止该读之后的写操作被重排到它前面,维持“先读后写”的逻辑顺序。
这使得线程一旦读到 flag == true,就能安全使用此前由写线程通过 volatile 写发布的所有状态(比如已初始化好的对象字段)。
屏障如何映射到底层 CPU 指令
不同架构生成不同指令,但语义一致:
- x86 平台常用
lock addl $0,0(%rsp)或mfence实现 StoreLoad; - ARM 架构用
dmb ish等内存屏障指令; - JVM 自动适配,开发者无需关心具体汇编,只需声明 volatile 即可触发对应屏障插入。
它不保证复合操作的原子性
volatile 不能替代 synchronized 处理 i++ 这类读-改-写操作:
-
volatile int count;中count++仍存在竞态——读、加1、写三步非原子; - 屏障只管单次读或单次写本身的顺序约束,不管跨操作的中间态;
- 需要原子性时,应配合 AtomicInteger 或加锁。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











