volatile不能保证原子性,因其仅保障可见性和有序性,不阻止复合操作(如i++)被多线程交错执行;i++包含读、改、写三步,即使每步可见,仍可能因并发读取相同旧值导致结果丢失。

volatile 不能保证原子性,是因为它只解决可见性和有序性问题,不解决操作本身的中间步骤是否被中断或交错执行的问题。比如 i++ 看似简单,实际包含“读取 i 的值 → 加 1 → 写回 i”三个步骤。即使每个步骤都用 volatile 修饰,多个线程仍可能同时读到同一个旧值,各自加 1 后写回,最终只加了一次,而不是预期的两次。
volatile 保证了什么?
它确保:
- 一个线程对 volatile 变量的修改,对其他线程立即可见(避免线程本地缓存导致的读旧值);
- 禁止编译器和处理器对该变量的读写进行重排序(维持代码逻辑的执行顺序)。
但它不提供锁机制,也不阻止多线程并发执行复合操作。也就是说,volatile 让你“看到最新的值”,但不帮你“安全地完成一连串动作”。
i++ 为什么不是原子操作?
以 volatile int i = 0; 为例,当两个线程同时执行 i++:
- 线程 A 读取 i = 0;
- 线程 B 也读取 i = 0(此时 A 还没写回);
- A 计算 0+1=1,写回 i=1;
- B 计算 0+1=1,写回 i=1。
结果是 i=1,而不是期望的 i=2。这个过程里,每次读、写都满足 volatile 的可见性,但整个 i++ 操作被“拆开交错执行”,丢失了一次递增。
如何真正保证原子性?
有几种常见方式:
- 用
java.util.concurrent.atomic.AtomicInteger,它的incrementAndGet()方法底层通过 CAS(Compare-And-Swap)指令实现原子更新; - 用
synchronized块包裹i++,让同一时刻只有一个线程能执行该段代码; - 使用
ReentrantLock显式加锁,效果类似 synchronized,但更灵活。
这些方案的核心是:把“读-改-写”三步变成一个不可分割的整体,而 volatile 单独做不到这一点。
不复杂但容易忽略:volatile 是轻量级的同步手段,适合状态标志(如 running = false)、单次写入后只读的场景;一旦涉及“读取并修改”,就必须升级到原子类或锁机制。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











