volatile 与内存可见性
2026/8/24大约 2 分钟
volatile 与内存可见性
volatile 的用途、与 synchronized 的对比,以及使用 volatile 的严格条件。
volatile 的用处
在当前 Java 内存模型下,线程可以把变量保存在本地内存(如寄存器)中,而不是直接在主存读写。这可能导致:一个线程在主存中修改了变量值,另一个线程还在使用自己寄存器中的旧拷贝,造成数据不一致。
将变量声明为 volatile 后,JVM 会:
- 每次访问时强制从共享内存重读该变量的值
- 变量变化时强制将变化值回写共享内存
这样任何时刻两个不同线程看到的都是同一个值。
volatile 与 synchronized 的对比
| 对比项 | volatile | synchronized |
|---|---|---|
| 同步强度 | 稍弱的同步机制 | 强同步机制 |
| 阻塞 | 不加锁、不阻塞线程 | 会阻塞等待锁 |
| 可见性 | ✅ 保证 | ✅ 保证 |
| 原子性 | ❌ 不保证 | ✅ 保证 |
关键点:
- 写入 volatile 变量相当于退出同步代码块,读取 volatile 变量相当于进入同步代码块
- volatile 只能保证可见性,不能保证原子性——
count++、count = count + 1不是原子操作,volatile 不起作用 - 过度依赖 volatile 会比用锁更脆弱、更难理解
使用建议
- 两个或更多线程需要访问的成员变量上使用 volatile
- 变量已在 synchronized 代码块中、或是常量时,没必要使用 volatile
- volatile 屏蔽了 JVM 必要的代码优化,效率较低,必要时才用
使用 volatile 的严格条件
当且仅当满足所有以下条件时才应使用:
- 对变量的写入操作不依赖变量的当前值,或你能确保只有单个线程更新变量的值
- 该变量没有包含在具有其他变量的不变式中
总结
需要同步时,第一选择应该是 synchronized,这是最安全的方式。尤其 JDK 1.5 之后对 synchronized 做了很多优化(自适应自旋锁、锁粗化、锁消除、轻量级锁),性能已大幅提升。