longadder高并发更快的根本原因是将单点竞争拆分为多点无锁操作;低并发时性能相近,中高并发吞吐量高出3–10倍;适用于写多读少、允许弱一致性的统计场景。

为什么LongAdder在高并发下比AtomicLong快
根本原因不是“更高级的算法”,而是它把单点竞争(value)拆成了多点无锁操作(Cell[]数组)。AtomicLong所有线程抢同一个value,CAS失败就自旋重试——并发一上来,CPU就在空转。LongAdder默认先试base(类似AtomicLong),一旦发现竞争(CAS失败),就懒加载扩容Cell数组,让不同线程落到不同槽位操作,冲突概率指数级下降。
- 低并发(比如 ≤ 4 线程):两者性能几乎一样,LongAdder甚至略慢一点(多一层判断+可能的数组分配)
- 中高并发(≥ 16 线程):LongAdder吞吐量可高出 3–10 倍,实测 1000 线程各增 1w 次,耗时差距常达一个数量级
- 不是“替代”,而是“分场景”:如果你只做一次计数、或更新极少,用
AtomicLong更轻量;如果是QPS监控、限流计数、日志埋点等高频写场景,LongAdder才是正解
add() 和 sum() 的语义陷阱你踩过吗
LongAdder.add() 是无锁、非阻塞、最终一致的;但sum() 不是实时快照——它要遍历所有Cell并加总base,期间其他线程可能还在写。这意味着:
-
sum()结果可能比“上一秒刚写的值”还小(因 Cell 更新尚未被读到,或遍历时有新写入但未计入) - 它不保证强一致性,不能用于需要精确临界判断的逻辑(比如“达到1000就触发告警”——要用
AtomicLong+ 显式同步,或改用带回调的限流器) -
sumThenReset()也不是原子的:sum完再reset中间有窗口,两次调用之间可能漏掉若干次增量
reset() 为什么基本等于“摆设”
LongAdder.reset() 文档里明确写了:“only effective if there are no concurrent updates”。现实中几乎无法满足这个前提——只要有一个线程正在执行add(),reset() 就可能清掉某个Cell的值,而另一个线程刚把新值写进那个Cell,结果就是“清零失败”或“计数错乱”。
- 别在定时任务里直接调
reset()来实现“每秒计数”——改用sumThenReset()更安全(虽然仍有窗口,但至少是“先取后清”) - 真要精确周期统计,建议用
LongAdder+ 时间戳打点,或者换ConcurrentHashMap<long longadder></long>按时间分桶 - 如果业务逻辑必须强一致 reset,说明
LongAdder并不适合你,回头用AtomicLong配synchronized或StampedLock更稳妥
什么时候不该用LongAdder
它不是万能计数器,几个典型误用场景:
立即学习“Java免费学习笔记(深入)”;
- 需要
getAndIncrement()返回旧值?LongAdder没有这类方法,只有increment()和add(),不返回当前值 - 要和别的变量一起做原子更新(比如“计数+更新状态位”)?
LongAdder不支持复合操作,得回退到AtomicReferenceFieldUpdater或VarHandle - 内存敏感环境(如嵌入式、函数计算冷启动)?它默认最多创建 2^16 个
Cell,每个Cell含 padding 防伪共享,单个实例常驻内存远超AtomicLong(约 1KB+) - Java 7 或更低版本?
LongAdder是 JDK 8+ 特性,老项目升级前先确认JDK版本
真正关键的一点是:LongAdder解决的是“写多读少、允许弱一致性读”的计数问题。一旦你开始纠结“这次sum到底准不准”“reset能不能刚好卡在毫秒边界”,那问题往往不在工具选错,而在模型本身没对齐——先想清楚你要的到底是“统计趋势”,还是“精确状态”。










