volatile无法保证线程安全,根本原因在于它只保证可见性和有序性,不保证“读—改—写”复合操作的原子性;i++被拆为三步,多线程可能同时读到相同值并覆盖写入,导致数据丢失。

Java 中对计数器做 i++ 操作,即使变量用 volatile 修饰,依然无法保证线程安全,根本原因在于:volatile 只管“读得新、写得快、顺序不乱”,但不管“读—改—写”这三步能不能一起完成。
volatile 管什么?
它确保两件事:
- 每次读取都从主内存拿最新值(可见性)
- 每次写入都立刻刷回主内存,且禁止该写与后续读/写重排序(有序性)
i++ 实际做了三件事
哪怕只写一行代码 count++,JVM 也会拆成三条独立指令执行:
- 从主内存(或工作内存同步后)读取
count的当前值 - 在 CPU 寄存器中加 1
- 把新值写回
count所在内存位置
volatile 能保证第一步读的是最新值、第三步写完立刻可见,但它**无法锁住中间过程**——两个线程可能同时读到 5,各自加 1 后都写回 6,结果只累加了一次。
为什么 AtomicInteger 就可以?
它底层用的是 CAS(Compare-And-Swap),把“读—改—写”封装成一条由 CPU 硬件保障的原子指令(如 x86 上的 lock xadd)。这个操作要么完整成功,要么失败重试,不会被线程切换打断。
什么情况下可以用 volatile?
适合纯状态标志,比如:
-
volatile boolean running = true;—— 仅用于控制循环启停 -
volatile boolean initialized = false;—— 仅用于表示是否完成初始化
只要不依赖旧值做计算(如 if (flag) list.add(x) 这类复合逻辑),volatile 就够用;一旦涉及“先读再算再写”,就必须换 AtomicInteger、synchronized 或 ReentrantLock。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











