你问的是 Java 并发模型里最核心、也最容易被误解的一个关键字:
volatile。
它的底层不是“禁止缓存”,也不是“强制刷新内存”,
它的本质是:
通过 CPU 内存屏障(Memory Barrier)和 JMM(Java 内存模型),建立可见性与有序性。
下面从 JVM → CPU → 指令重排三个层面拆开。
⸻
一、volatile到底解决什么问题?
它只解决两个问题:
能力 volatile
可见性 ✅
禁止指令重排 ✅(部分)
原子性 ❌
也就是说:
volatile int x;
保证:
• 一个线程写 x
• 另一个线程立刻能看到
但:
x++
仍然不是原子操作。
⸻
二、JVM 在 volatile 上做了什么?
对这段代码:
volatile int flag;
flag = 1;
JIT 编译后,会生成类似:
mov [flag], 1
StoreStore barrier
StoreLoad barrier
对读取:
int x = flag;
生成:
LoadLoad barrier
LoadStore barrier
mov rax, [flag]
你看到的不是 Java 魔法,而是:
JVM 在 volatile 读写前后插入 CPU 内存屏障指令
⸻
三、CPU 内存屏障干了什么?
现代 CPU 有:
• L1 / L2 / L3 cache
• store buffer
• out-of-order execution
所以:
Thread A 写 flag=1
Thread B 可能还看到 flag=0
内存屏障的作用是:
强制 CPU 刷新写缓冲区,并禁止乱序执行跨过这个点
也就是说:
volatile 写
→ 所有之前的写入必须对其他核心可见
volatile 读
→ 所有之后的读必须从主内存重新加载
这叫:
happens-before
⸻
四、为什么 volatile 能解决 DCL 单例问题?
volatile Singleton instance;
if (instance == null) {
synchronized(...) {
if (instance == null) {
instance = new Singleton();
}
}
}
如果没有 volatile,JVM 允许:
1. 分配内存
2. 把地址赋给 instance ← 乱序
3. 调用构造函数
另一个线程看到:
instance != null
但对象还没构造完
volatile 禁止这条重排:
写 volatile instance
之前的构造
必须完成
⸻
五、在 JDK 21+(包括 Loom)它仍然有效吗?
是的,而且更重要。
虚拟线程依然运行在多核 CPU 上,
它们共享同一套缓存一致性协议。
volatile 作用的是:
核心之间的可见性
跟你是不是虚拟线程无关。
⸻
六、为什么说 volatile 是“最低成本的并发同步”?
因为它:
• 不阻塞
• 不切线程
• 不进内核
• 只插入几条 CPU fence
代价只有几个纳秒。
所以它适合:
• 状态标志
• 配置开关
• 是否关闭服务
• 是否初始化完成
⸻
七、一句话总结
volatile 的底层不是 JVM 魔法,而是:
JVM 用 CPU 内存屏障,把你的读写升级成“跨核心可见的有序访问”。
它是你能用到的最底层、最便宜的并发语义。