volatile仅保证单次读/写操作的原子性,如long/double的64位完整读写,但不保证i++等复合操作原子性;其原子性由JMM规范、编译器禁止拆分及硬件lock指令协同保障。

volatile 本身不提供“原子性”这个概念的完整保障,但它对单个变量的读或写操作,在 Java 内存模型(JMM)层面被定义为具有原子性。这种原子性不是靠锁或 CAS 实现的,而是由语言规范和底层硬件协同保证的。
volatile 对单个读/写操作的原子性定义
Java 规范明确指出:对 volatile 变量的单次读操作或单次写操作是原子的。这意味着:
- 不会出现“半个 long”或“半个 double”的读写问题(即 64 位变量在非 volatile 下可能被拆成两次 32 位操作,而 volatile 禁止这种拆分);
- 一次 volatile 读,要么拿到完整、一致的值,要么不读;一次 volatile 写,要么完整写入主内存,要么不写;
- 这种原子性是 JVM 层面的语义承诺,无需程序员额外同步。
为什么能保证单个读/写的原子性?
这背后依赖三个层次的配合:
- JMM 规则强制:JSR-133 修订后,volatile 的读写被赋予与监视器锁(synchronized)类似的 happens-before 语义,其中就隐含了对单个操作的原子执行要求;
- 编译器禁止拆分:JVM 编译器不会将 volatile long/double 的读或写优化为两条 32 位指令;
-
硬件指令支持:x86 平台下,volatile 写通常编译为带
lock前缀的指令(如lock xchg),该指令天然具备原子性;ARM 等平台则通过内存屏障 + 原子加载/存储指令(如ldar/stlr)实现等效效果。
注意:原子性仅限于“单个读”或“单个写”
以下操作 都不属于原子操作,volatile 无法保证:
-
i++(读 + 计算 + 写,三步); -
a = b + c(读 b、读 c、加法、写 a,多步); - 对数组元素或对象字段的 volatile 修饰,不延伸至整个对象结构的操作。
实际意义与使用建议
这种“单读/单写原子性”主要解决两类问题:
- 避免 64 位变量(
long、double)在 32 位 JVM 或多核环境下的“字撕裂”(torn write); - 作为状态标志(如
volatile boolean running = true),确保状态切换被其他线程准确、完整地看到——此时读和写各自都是原子的,且组合起来具备可靠的可见性与有序性。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











