直接用 LongAdder 替换 AtomicLong 可显著提升高并发计数性能,因其将单变量拆为多独立槽位,减少 CAS 自旋;适用于只累加、不强读、写远多于读的场景。

直接用 LongAdder 替换 AtomicLong 就能显著提升高并发计数性能,核心在于它把“一个热点变量”拆成“多个独立单元”,让线程各自操作不冲突的槽位,避免 CAS 自旋浪费 CPU。
适用场景:只累加、不强读、写远多于读
不是所有计数都适合换 LongAdder。它专为以下情况设计:
- 只做
increment()、add(x)这类累加/累减,不依赖中间精确值(比如统计总请求数、错误次数、QPS) - 更新非常频繁,但调用
sum()的次数很少(例如每分钟汇总一次,而非每次请求都查) - 允许最终一致:
sum()返回的是当前所有槽位的快照和,不是某个原子时刻的精确值(并发写入中调用可能略滞后) - 不能用于控制逻辑,比如
while (counter.sum() 这种循环条件——因为值非实时,可能导致死循环或漏判
替换方式:简单但注意语义变化
代码改动极小,但关键细节不能忽略:
- 声明从
private final AtomicLong counter = new AtomicLong();改为private final LongAdder counter = new LongAdder(); -
counter.incrementAndGet()→counter.increment();(increment()无返回值) -
counter.get()→counter.sum()(注意:不是get(),且结果非强一致) - 如果需要初始化非零值,不能直接构造,得先
new LongAdder()再add(initValue)
原理简析:分段 + 避伪共享 = 减竞争
它不是靠锁或单点 CAS,而是靠两层机制降低争抢:
立即学习“Java免费学习笔记(深入)”;
-
base + cells 分治结构:低并发时所有线程尝试更新公共
base;一旦发现竞争(CAS 失败多次),就扩容cells数组,线程按哈希分散到不同Cell槽位写入,彼此互不干扰 -
@Contended 填充防伪共享:每个
Cell内部用 JVM 注解自动填充字节,确保每个Cell.value单独占满一个 CPU 缓存行(64 字节),避免不同核修改相邻变量时引发缓存行反复同步
实际效果:线程越多,优势越明显
在 256 线程并发递增场景下,LongAdder 耗时约 220ms,而 AtomicLong 达 5800ms —— 性能差距达 26 倍。这不是理论优化,是真实可测的吞吐提升。但也要注意:线程数少(如 ≤4)时,AtomicLong 反而更轻量;若需高频读取累计值,LongAdder 的遍历求和开销会抵消部分写入优势。



















