Java中避免i++线程安全问题,须用AtomicInteger(基于CAS)或synchronized,因i++非原子操作;volatile仅保可见性,无法解决原子性缺陷。

Java 中避免 i++ 的线程安全问题,关键在于理解它本质是“读-改-写”三步非原子操作,而非简单地加一。多个线程并发执行时,可能同时读到旧值、各自加一、再写回相同新值,导致结果丢失。解决思路不是强行“修复” i++,而是换用真正支持原子性的机制。
用 AtomicInteger 替代普通 int
这是最常用、推荐的方案。AtomicInteger 内部基于 CAS(Compare-And-Swap)指令实现无锁原子更新,性能好且语义清晰。
- 把
int count = 0;换成AtomicInteger count = new AtomicInteger(0); - 把
count++;换成count.incrementAndGet();(返回自增后值)或count.getAndIncrement();(返回自增前值) - CAS 在底层由 CPU 硬件指令保证,即使多线程同时尝试更新,也只会有一个成功,其余自动重试,全程无锁、无阻塞
用 synchronized 保护临界区
适用于已有较大同步块、或需要将自增与其他操作组成原子事务的场景。虽然开销比 AtomicInteger 大,但逻辑直观、可控性强。
- 确保所有对共享变量的读、改、写操作都包裹在同一个同步块中,锁对象要一致(如用
this、类对象或专用锁对象) - 例如:
synchronized (lockObj) { count++; },这样任意时刻只有一个线程能进入该块 - 注意不要用
String.class或常量字符串作锁,易引发意外锁竞争;优先使用私有 final 对象
避免误用 volatile 修饰自增变量
volatile 能保证可见性,但不能解决 i++ 的原子性缺陷——它无法阻止两个线程先后读到同一旧值、各自计算、再覆盖写入。
立即学习“Java免费学习笔记(深入)”;
- 声明
volatile int count;后执行count++;,仍会出错(实测结果远小于预期值) - volatile 正确用途是:单写多读的状态标志(如
volatile boolean running),或作为 DCL(双重检查锁定)中的实例引用 - 只要涉及“读取旧值→计算→写入新值”的复合操作,volatile 就不适用
根据场景选择合适工具
不必死守一种方式。实际开发中应结合操作粒度、性能要求和代码结构来选:
- 纯计数器、高并发累加:首选
AtomicInteger或LongAdder(后者在极高并发下性能更优) - 需与数据库更新、日志记录等其他步骤绑定为一个逻辑单元:用
synchronized或ReentrantLock - 仅用于通知状态变化(如开关控制):
volatile boolean完全够用,轻量又高效


















