volatile修饰long/double可保证单次读写原子性,但仅限于禁止重排序、强制主内存访问以避免值撕裂;它不保证i++等复合操作原子性,现代jdk中普通读写已默认原子,其核心价值仍在可见性与有序性。

Java 中用 volatile 修饰 long 和 double,确实能保证读写操作的原子性——但这仅针对 64 位 JVM 且未开启分拆写(如某些旧 JVM 或特殊配置)的常规场景。本质上,volatile 的作用不是“让非原子变原子”,而是**禁止重排序 + 强制主内存读写 + 消除缓存不一致**,从而间接保障了 64 位变量的原子读写语义。
为什么 long/double 默认可能非原子?
Java 规范允许 JVM 将 64 位变量(long、double)的读写拆成两个 32 位操作(尤其在 32 位 JVM 上)。这意味着一个线程可能只看到“半更新”的值(比如高位是旧值、低位是新值),造成数据错乱。虽然现代主流 JVM(如 HotSpot 64 位)默认以原子方式处理它们,但该行为**不被 JMM 保证**,除非显式加 volatile 或用同步机制。
volatile 如何起作用?
volatile 告诉 JVM:这个变量必须从主内存直接读、直接写,不能仅靠线程本地缓存;同时插入内存屏障,阻止指令重排序。这样一来:
- 读操作变成一次完整的 64 位加载(不会被拆成两次 32 位读)
- 写操作变成一次完整的 64 位存储(不会被拆成两次 32 位写)
- 其他线程一定能看到最新写入的完整值,不会出现“撕裂”现象
实际写法与注意事项
声明很简单:
private volatile long timestamp; private volatile double balance;
但要注意:
-
volatile只保证单次读/写的原子性,不保证复合操作(如i++)的原子性——这仍需synchronized或AtomicLong - 它不提供互斥,仅解决可见性和有序性问题
- 在 JDK 5+ 的 JMM 下,
volatile对long/double的原子性保障是明确规定的,无需额外配置
替代方案对比
如果需要更复杂的原子操作(如 CAS、累加),推荐使用 java.util.concurrent.atomic 包:
-
AtomicLong:支持incrementAndGet()、compareAndSet()等 -
AtomicDouble(需自行封装或用DoubleAdder) - 普通
volatile更轻量,适合仅需“读最新值”或“写后立即可见”的场景
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











