LongAdder比AtomicLong更适合超高并发计数,因其通过分段累加(base+cells数组)分散CAS竞争,降低线程争用;而AtomicLong依赖单点CAS,在高并发下频繁失败重试,引发CPU空转和缓存行失效。

在超高并发场景下,LongAdder 比 AtomicLong 更适合做计数器,核心原因是它通过“分段累加 + 最终合并”的策略,显著减少了线程间对共享变量的争用。
为什么 AtomicLong 在高并发下会成为瓶颈
AtomicLong 底层依赖 CAS(Compare-and-Swap)操作更新单个 volatile 变量。当大量线程同时调用 incrementAndGet() 时,绝大多数 CAS 会失败并重试,造成大量 CPU 空转和缓存行频繁无效化(false sharing),性能随线程数增加急剧下降。
LongAdder 是怎么解决这个问题的
LongAdder 不把所有增量压到一个变量上,而是维护一个 base 值 + 一个 cells 数组。每个线程尝试更新时:
- 先尝试用 CAS 更新 base,失败则进入分段逻辑
- 根据当前线程的 probe 值定位到 cells 数组某个槽位
- 对该槽位执行 CAS 更新;若槽位为空或 CAS 再次失败,则可能扩容 cells 数组
- 读取总值时,把 base 加上所有非空 cells 的值
这种设计让多个线程大概率操作不同内存位置,极大降低竞争。
立即学习“Java免费学习笔记(深入)”;
什么时候该用 LongAdder 替换 AtomicLong
适用场景非常明确:
- 只做“累加”或“累减”,不依赖实时精确值(如统计 QPS、请求总数、错误次数)
- 写多读少:更新频率远高于调用
sum()的频率 - 不要求强一致性:
sum()是最终一致,不是原子快照(例如并发更新中调用 sum() 可能漏掉正在写入的值) - 不用于循环条件或同步控制(比如不能代替 while 循环里的计数判断)
实际替换示例与注意事项
替换很简单,但要注意语义差异:
// 替换前
private final AtomicLong counter = new AtomicLong();
public void record() {
counter.incrementAndGet();
}
public long getCount() {
return counter.get();
}
// 替换后
private final LongAdder counter = new LongAdder();
public void record() {
counter.increment(); // 注意:没有返回值
}
public long getCount() {
return counter.sum(); // 不是 get()
}
关键点:
-
LongAdder的increment()、add(long)都无返回值,无法像AtomicLong.incrementAndGet()那样直接获取新值 -
sum()开销略高于get(),因为它要遍历 cells;如果读非常频繁,需权衡 - 可调用
reset()归零(内部是清空 cells 并重置 base),比反复新建对象更轻量 - 不支持 compareAndSet 等原子条件操作,无法替代需要 CAS 语义的逻辑



















