volatile不能保证原子性,因其仅保障单次读/写操作的可见性与有序性,而i++等复合操作在jvm中被拆解为读-改-写三步,缺乏互斥机制,导致多线程下丢失更新。

volatile 不能保证原子性,是因为它只干预单次读或写操作的可见性和执行顺序,不控制多步操作的整体执行过程。
它只管“单步”,不管“三步”
像 i++ 这样的操作,在 JVM 层面实际拆成三步:
- 从主内存或工作内存中读取 i 的当前值
- 在 CPU 寄存器中执行 +1 计算
- 把新值写回内存
volatile 能确保每次读(get)和每次写(put)都直接与主内存交互,但它无法让这三步“绑在一起”执行。两个线程可能同时读到 i = 5,各自加 1 后都写回 6 —— 一次更新就丢失了。
底层没有互斥或硬件级原子指令支持
volatile 的实现依赖内存屏障(如 StoreLoad、LoadStore),这些指令只约束指令执行顺序、防止重排,并强制缓存同步。它们不提供锁机制,也不调用 CPU 的原子指令(如 x86 的 lock xadd)。而 AtomicInteger.incrementAndGet() 能成功,正是因为它封装了 CAS 操作,由硬件直接保障“比较并交换”这一整套动作不可中断。
适用边界非常明确
volatile 安全的前提是:操作不依赖旧值,也不产生中间状态。例如:
- 状态标志位:volatile boolean running = true;一个线程设为 false,其他线程立即看到并退出循环
- 一次性发布配置:volatile String env = "prod"; 后续只读不改
- 配合 final 或锁做安全初始化:如双重检查单例中的 instance 字段
一旦出现“读旧值 → 算新值 → 写结果”这类逻辑,就必须换用 synchronized、ReentrantLock 或 AtomicInteger。
不是性能妥协,而是职责不同
volatile 不是“轻量级 synchronized”,它解决的是可见性和有序性问题;synchronized 和原子类解决的是执行控制和原子性问题。混淆二者,就像用螺丝刀拧螺母——看起来都在“固定东西”,但原理和适用场景完全不同。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











