volatile 能保证什么?
未标记
考查重点:可见性、有序性与原子性。volatile 保可见性和有序性,i++ 仍非原子;复合更新用锁或原子类
本题目录
参考答案
核心回答
Java 的 volatile 主要保证共享字段读写的可见性,以及 Java 内存模型要求的有序性。它不提供互斥,不能把 i++、先检查再修改等复合操作变成一个原子操作。以下依据 Java SE 25 规范说明。
- 可见性:对一个
volatile字段的写,happens-before于同步顺序中后续对同一字段的读。结合程序顺序和传递性,写线程在这次发布之前的普通写,也能对读线程随后执行的读取可见。 - 有序性:编译器和处理器不能通过重排序破坏上述同步效果。它约束的是相关操作的可观察行为,并非让所有代码都禁止重排,也不是把两个线程强制排成先后执行。
- 原子性边界:单次
volatile读或写是原子的,包括volatile long和volatile double;但“读、计算、写”组合起来仍可被其他线程穿插。
所以它适合状态标志和安全发布等场景。多个线程同时累加计数,可以用 AtomicInteger.incrementAndGet();若要一起维护多个字段的业务约束,通常需要锁或其他完整的同步方案。
进一步解释
为什么可见性仍解决不了 i++?
假设计数器当前为 0,两个线程都执行自增。它们可能先后读到 0,各自在本地算出 1,然后分别写回 1。每次读写都符合 volatile 规则,最后仍只增加了 1。
问题是两次自增的步骤发生了交错,不是“另一个线程没有及时刷新缓存”。需要保证整次自增不可分割,而不只是分别保证读取和写入。类似地,把余额声明为 volatile,也不能让“余额足够就扣款”自动成为原子操作。
volatile 怎样连带发布普通数据?
以一次性初始化为例:ready 初始为 false,唯一的发布线程先准备好普通字段中的数据,再把 volatile ready 置为 true。读线程读到这个 true 后,才去读取那些数据;在没有后续并发改写的前提下,就能看到发布前完成的初始化。
原因是:准备数据先于写 ready,写 ready 又先于相应的读,读线程再读取数据,通过 happens-before 的传递性把这些操作连接起来。
关键是读写同一个 volatile 字段,并遵守先准备、后发布、先观察发布、后使用数据的顺序。写一个字段、读另一个无关字段,不能仅凭“它们都有 volatile”建立这种关系。
底层是不是每次都直接读写主内存?
这只是容易误导的简化说法。JMM 定义的是程序允许观察到的结果;JVM 会结合目标处理器的内存模型、内存屏障和缓存一致性机制实现这些保证。具体指令和屏障能否合并、省略,取决于 JVM、处理器和优化结果,不能把某种架构的实现写成 Java 的统一指令规则。
volatile 也不提供线程调度或通知机制。用它作停止标志,工作线程仍要继续执行并再次检查标志;它不会自动唤醒一个阻塞中的线程。
常见追问
引用是 volatile,对象里的字段也都线程安全吗?
不会。通过 volatile 引用发布一个已经初始化好的对象,可以让读到该引用的线程看到发布前的初始化结果;发布后多个线程继续修改对象内部的普通字段,仍需要合适的同步。volatile 数组引用也不会让每个数组元素自动具有 volatile 读写语义。常见做法是发布不可变配置快照,更新时整体替换引用。
双重检查锁的单例为什么还需要 volatile?
外层检查和快速返回没有持有初始化使用的锁,实例引用需要安全发布,避免另一个线程看到非空引用却看不到完整初始化结果。volatile 解决发布可见性和顺序,锁与内层检查负责协调多个初始化线程;只加 volatile 仍不能让“检查为空再创建”成为原子操作。
参考资料
- 国内:JavaGuide:Java 并发常见面试题(二):采用 volatile、happens-before 和自增交错部分,解释它能发布什么,以及可见性为什么不能替代复合操作的原子性。
- 国内:JavaGuide:Java 内存模型详解:采用程序顺序、volatile 与传递规则,把普通数据经标志字段发布的保证串起来,并区分 JMM 抽象和物理内存。
- 国内:沉默王二:Java volatile 关键字解析:采用状态发布和自增交错的例子,说明适用场景;具体重排序边界以语言规范为准。
- 国外:Java SE 25 语言规范:Threads and Locks:依据 §17.4.4–§17.4.5 核对同步顺序、同一字段和 happens-before,依据 §17.7 核对 volatile long、double 单次读写的原子性。
- 国外:Jeremy Manson、Brian Goetz:JSR-133 FAQ:采用 volatile 的发布与获取、匹配同一字段及双重检查锁解释,补充普通数据安全发布的条件。

