LongAdder 仅在写多读少场景下比 AtomicInteger 更快,适用于频繁 increment 但极少 sum 的计数;若读写均衡或需强一致性,则 AtomicInteger 更稳妥。

LongAdder 比 AtomicInteger 快,但只在写多读少时成立
不是所有“高并发计数”都该换 LongAdder。它真正的优势场景是:**大量线程频繁调用 increment(),但 sum() 很少被调用**(比如每秒统计一次 QPS,而不是每次请求都取总数)。这时 LongAdder 通过分段 Cell 数组把竞争打散,避免 CAS 自旋浪费 CPU;而 AtomicInteger 所有线程争抢同一个变量,高并发下失败重试成本陡增。
- 实测常见场景(20 线程 × 1000 万次自增):
LongAdder耗时通常比AtomicInteger低 30%–60% - 但若你每自增一次就立刻调用
sum(),性能反而可能更差——因为sum()需遍历所有 Cell,且不保证强一致性 -
AtomicInteger内存占用小(单个 volatile int),LongAdder初始就占多个Cell,高并发下还会动态扩容,内存开销明显更大
不能直接替换 AtomicInteger —— 类型、API 和语义都不一样
LongAdder 是 long 类型专用,没有 int 版本;它不提供 getAndIncrement()、compareAndSet() 这类原子读-改-写操作,也没有初始值构造函数(只能默认从 0 开始)。最关键的是:sum() 返回的是**最终一致值,非实时值**。
- 错误用法:
if (counter.sum() > 100) { rejectRequest(); }—— 条件判断依赖实时性,这里可能漏判或误判 - 正确替代:
AtomicInteger的getAndIncrement()可用于限流令牌桶、序列号生成等需精确控制的逻辑;LongAdder不适合这类场景 - 想用
int做高并发累加?只能用LongAdder+ 手动截断(不推荐),或退回到AtomicInteger—— JDK 没提供IntAdder
使用 LongAdder 必须注意的三个坑
很多人一看到“性能更好”就全局替换,结果线上出问题。这三个点不检查,LongAdder 很容易变成隐患:
-
sum()不是原子快照:多个线程同时调用sum(),返回值可能不一致;它也不阻塞写入,所以两次sum()之间可能有未合并的增量 - 未调用
reset()就复用实例:如果把LongAdder当作“临时计数器”反复使用(比如每个 HTTP 请求 new 一个),记得调用reset()清空;否则 sum 会累积历史值 - JVM 参数遗漏导致伪共享失效:
Cell类用了@sun.misc.Contended注解防 CPU 缓存行伪共享,但必须启用 JVM 参数-XX:-RestrictContended(JDK8+ 默认关闭该参数,但某些容器环境会重置),否则性能提升大打折扣
什么时候该坚持用 AtomicInteger
别被“高并发”三个字带偏。如果你的计数器要参与业务逻辑判断、需要精确顺序、或者读写频率接近(比如每 5 次 increment 就 get 一次),AtomicInteger 仍是更稳的选择。
立即学习“Java免费学习笔记(深入)”;
- 典型场景:分布式 ID 生成器中的本地步长计数、状态机里的状态流转计数、资源池剩余数校验
- 调试友好:它的
get()值永远准确,日志打点、监控告警、问题排查都更可信赖 - 轻量确定:无扩容、无 Cell 分配、无额外 volatile 字段,行为完全可预测
高并发不是一味追求吞吐数字,而是看清楚“谁在读、谁在写、读写间隔多久、错一次代价多大”。LongAdder 是把一致性让渡给吞吐的妥协方案,用之前得先问自己:这个让渡,我真能接受吗?










