atomicinteger适合替代需线程安全自增/自减或条件更新的int变量,如计数器、序列号生成;不适用于需“读-改-写”整体原子性的复合操作,此时应使用compareandset或锁。

AtomicInteger 适合替代什么场景下的 int 变量
当多个线程需要对同一个整型计数器做自增、自减或条件更新,且你不想用 synchronized 块或 ReentrantLock 时,AtomicInteger 就是直接可用的轻量级替代。它不适用于需要复合操作(比如“先读再算再写”且中间不能被打断)的场景——那得靠 compareAndSet 手动实现,或者换锁。
常见误用:把它当普通 int 直接参与表达式运算,比如 i++ + 5,这会丢失原子性,因为 get() 和后续计算之间可能被其他线程修改。
- 适合:计数器、序列号生成、统计开关次数、限流器中的剩余请求数
- 不适合:需要保证“读-改-写”整个逻辑原子性的业务逻辑(如余额扣减后判断是否为负)
- 注意:
AtomicInteger的get()是 volatile 读,set()是 volatile 写,但不是所有方法都带内存屏障语义——比如lazySet()就只保证写不重排序,不保证立即对其他线程可见
incrementAndGet() 和 getAndIncrement() 的行为差异
这两个方法都执行原子自增,但返回值不同:incrementAndGet() 返回**自增后的值**,getAndIncrement() 返回**自增前的值**。这和 i++ 与 ++i 的语义一致,但它们是线程安全的。
错误示例:用 getAndIncrement() 当作“获取并用于后续计算”,却没意识到它返回的是旧值,导致逻辑错位。
-
counter.incrementAndGet()→ 比如原值是 9,返回 10 -
counter.getAndIncrement()→ 原值是 9,返回 9,之后才变成 10 - 性能上无明显差别,JVM 对两者优化程度接近,选哪个只取决于你需要新值还是旧值
用 compareAndSet 实现带条件的更新
compareAndSet(expectedValue, newValue) 是 AtomicInteger 的核心能力,也是实现无锁算法的基础。它只有在当前值等于 expectedValue 时才把值设为 newValue,并返回 true;否则返回 false,不修改值。
典型陷阱:写成循环但忘了重新读取当前值,导致 CAS 总失败。
int current;
do {
current = counter.get();
} while (!counter.compareAndSet(current, current * 2)); // 正确:每次重读
- 不能假设“我刚读到是 5,它就一直是 5”——必须用循环+重读来应对并发竞争
- CAS 失败不抛异常,只返回
false,漏掉这个判断会导致逻辑静默失效 - 如果预期值本身来自一次非原子读(比如从数据库查出来),那整个 CAS 就失去意义——它只保障本地变量和原子对象之间的比对
AtomicInteger 和 volatile int 的关键区别
volatile int 能保证可见性和禁止重排序,但**不保证操作的原子性**。比如 i++ 在 volatile 变量上仍然是三步(读、加1、写),中间可能被其他线程插入修改。
而 AtomicInteger 的所有更新方法(incrementAndGet、addAndGet、compareAndSet)都基于 CPU 的 CAS 指令(x86 上是 lock xadd 或 lock cmpxchg),由硬件保障原子性。
- volatile int:适合“只写不改”或“只读不依赖中间态”的场景(如开关标志
running = false) - AtomicInteger:适合“读-改-写”必须整体原子的场景(如计数、ID 分配)
- 别混用:不要用
volatile int做计数器,也不要以为给AtomicInteger加volatile修饰能增强什么——它内部已经用 volatile 字段实现了语义
实际用的时候,最易忽略的是 CAS 循环中忘记刷新当前值,或者把 get() 结果直接拿去参与非原子计算。这些地方一出错,问题往往偶发、难复现,调试成本远高于加个锁。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










