volatile 不保证 long/double 复合操作原子性,仅强制单次读写原子;其核心价值是可见性与有序性,如状态标志位。

volatile 本身不“保证”long 和 double 的原子性,而是 JVM 规范强制要求:只要用 volatile 修饰了 long 或 double 变量,其单次读(get)和单次写(put)操作就必须是原子的。
为什么需要 volatile 来兜底原子性?
在 32 位 JVM 上,long/double 是 64 位数据,而寄存器和总线宽度通常只有 32 位。JVM 可能将一次写拆成两个独立的 32 位操作:先写高 32 位、再写低 32 位(或相反)。若此时另一个线程正在读,就可能读到“高半截是旧值、低半截是新值”的撕裂值(torn write),比如 0x80000000_00000001 变成 0x80000000_FFFFFFFF 这类非法组合。
volatile 的作用是告诉 JVM:“这个变量必须以原子方式读写”,于是 JVM 会通过以下任一方式实现:
- 在支持的硬件上使用单条 64 位指令(如 x86-64 的 movq)
- 在不支持的平台(如某些嵌入式 32 位 CPU)上加内存屏障 + 锁总线 / CAS 自旋等机制
注意:仅限单次读或单次写
volatile 不让 复合操作 变成原子。例如:
-
counter = 100L✅ 原子赋值(volatile 有效) -
counter++❌ 非原子:先读、再加 1、再写回 —— 三步之间可被其他线程打断 -
counter += 5❌ 同样是非原子的读-改-写序列
这类场景必须用 AtomicLong、synchronized 或显式锁,volatile 无能为力。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
现实中你大概率不需要它来保原子性
自 Java 5 起,主流 JDK(HotSpot/OpenJDK)在 64 位 JVM 上已默认对 long/double 普通读写做原子处理;现代 32 位 JVM(如 OpenJDK 8u292+、11、17、21)也通过底层指令规避了撕裂风险。
也就是说:除非你明确运行在老旧嵌入式环境(如 ARMv5/32 位 JVM、定制精简版 JRE),否则 volatile 对 long/double 的“原子性兜底”只是规范留下的安全边界,实际并不触发。
它的真正价值在可见性与有序性
volatile 的核心能力始终是:
- 每次读都从主内存加载(不缓存副本)
- 每次写都立即刷回主内存(不延迟写入)
- 禁止编译器和 CPU 对 volatile 读写前后指令重排序
典型用途是状态标志位:volatile boolean isRunning = true;,而不是靠它做计数或运算。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










