AtomicInteger 的 incrementAndGet 比 synchronized 更快,因其基于 CPU 的 CAS 指令,避免线程挂起与调度开销;synchronized 在竞争激烈时触发操作系统级锁,而 CAS 多数情况一次成功,仅失败时重试。

AtomicInteger 的 incrementAndGet 为什么比 synchronized 更快
因为它是基于 CPU 的 CAS(Compare-And-Swap)指令实现的,不涉及线程挂起和调度开销。synchronized 在竞争激烈时会触发操作系统级锁,而 incrementAndGet 只在 CAS 失败时重试,多数情况下一次就成。
但要注意:CAS 不是万能的。如果某个线程长期自旋失败(比如高并发下总被其他线程抢先修改),反而会浪费 CPU。这时候得看实际压测数据,而不是默认“无锁一定更好”。
- 适用于读多写少、单次操作简单(如计数、标志位)的场景
- 不适合复合逻辑,比如“先读再判断再写”,
get()+set()组合不是原子的 - 在 JDK 8+ 中,
incrementAndGet()底层调用的是Unsafe.compareAndSetInt(),依赖硬件支持
compareAndSet 和 getAndIncrement 的行为差异
compareAndSet(expected, update) 是条件更新:只有当前值等于 expected 才设为 update,返回 true;否则返回 false,不修改值。它让你能自己控制重试逻辑。
getAndIncrement() 是无条件自增:先返回旧值,再加 1,整个过程原子。它内部就是用 compareAndSet 循环实现的。
立即学习“Java免费学习笔记(深入)”;
- 需要“乐观锁语义”时用
compareAndSet,比如实现简易状态机(仅从 READY → RUNNING) - 单纯计数用
incrementAndGet或getAndIncrement更简洁 -
compareAndSet返回布尔值,必须显式处理false分支,否则可能丢更新
AtomicInteger 在 Spring Boot 中当计数器用的常见坑
很多人把 AtomicInteger 声明为类成员变量,然后在 Controller 方法里直接调用 incrementAndGet()——这本身没错,但容易忽略两点:
- Spring 默认是单例 Bean,多个请求共享同一个
AtomicInteger实例,计数是全局的;如果想按用户/请求维度隔离,得用ThreadLocal<atomicinteger></atomicinteger>或存到 request scope - 如果把它放在 @Configuration 类里作为 static 字段,重启应用不会重置,但 JVM 停止后就没了,别误以为是持久化计数
- 日志中打印
atomicInteger.get()没问题,但别在循环里高频调用get()然后做 if 判断——那块逻辑可能已经过期了
替代方案:什么时候不该用 AtomicInteger
当你要做的不只是“加一减一”,而是带业务校验的更新(比如余额不能为负),或者要同时更新多个字段,AtomicInteger 就撑不住了。
- 多个原子变量之间没有原子性保证:改
count和改lastModified两个AtomicLong,无法保证它们一起成功或一起失败 - 需要事务语义(如数据库一致性)时,必须回退到数据库乐观锁或分布式锁
- 数值超过
Integer.MAX_VALUE后incrementAndGet()会绕回Integer.MIN_VALUE,不会报错;如需防溢出,得自己封装或换用AtomicLong
高并发计数看着简单,真正上线后常卡在边界条件上:比如灰度发布时新老逻辑共存、监控采样率突变、甚至 GC 导致 CAS 自旋时间过长。这些地方没法靠 API 文档看出问题,得靠线上指标反推。










