64位JVM上long/double读写默认原子,32位JVM可能非原子;volatile可保证单次读写原子性及可见性,但复合操作仍需AtomicLong或synchronized。

64 位变量(long 和 double)在 JVM 中的读写是否原子,取决于虚拟机实现和运行环境,而不是 Java 语言本身强制要求。Java 内存模型(JMM)对它们做了特殊宽松处理,但现实中绝大多数场景下你完全不必担心非原子性问题。
什么是 long/double 的“非原子性协定”
Java 内存模型规定:对变量的 read、load、store、write 这四个操作必须具备原子性。但对未用 volatile 修饰的 64 位数据类型(long、double),允许 JVM 将单次 64 位操作拆成两次 32 位操作执行——即可能先写高 32 位、再写低 32 位,中间被其他线程打断,导致读到“半个变量”的值(例如高 32 位是旧值、低 32 位是新值)。
这个约定叫 Nonatomic Treatment of double and long Variables,本质是 JMM 为兼容早期 32 位硬件和 JVM 实现留下的弹性空间。
哪些情况真会出现非原子行为
实际发生需同时满足多个苛刻条件:
- 运行在 32 位 JVM 上(现代开发环境几乎绝迹)
- 变量未声明为
volatile - 多个线程频繁且无同步地对该变量进行读-改-写(如
counter++)或纯读写 - 硬件层面不保证 64 位内存访问的原子性(如某些老旧 x86 处理器未对齐访问)
即使全部满足,出现“撕裂读”(torn read)仍是小概率事件,调试困难、复现极难,更多是理论风险。
商用 JVM 实际怎么处理
主流 JDK(HotSpot)从很早开始就默认将 long 和 double 的读写实现为原子操作,无论 32 位还是 64 位 JVM:
- 64 位 JVM 天然支持原子性 64 位内存访问,无需拆分
- 32 位 JVM 在 HotSpot 中也通过字节码插入内存屏障或使用 CAS 指令等方式保障原子性
- Oracle JDK、OpenJDK、Zulu 等主流发行版均不公开暴露该非原子行为
换句话说:只要用的是近十年发布的标准 JDK,long 和 double 的普通读写就是安全的——不需要加 volatile 来“补救”原子性。
什么时候仍需考虑 volatile 或其他同步手段
volatile 对 long/double 的价值从来不是解决原子性,而是保障可见性和禁止重排序:
- 若变量被多线程共享且需及时看到最新值(如状态标志
isRunning = true),volatile仍是轻量级首选 - 若涉及复合操作(如
value += 1、max = Math.max(max, newVal)),必须用synchronized或AtomicLong,因为+=本身是非原子的(读+算+写三步) -
volatile long能确保读写原子性(JMM 明确要求),但仅限于单次读或单次写;它不能让i++变成原子操作










