bigywater.tech
Java 内部原理 · 22

LongAdder 比 AtomicLong 快多少:16 线程下真实测出 40 倍以上

"高并发计数场景用 LongAdder 别用 AtomicLong"是常见建议,但快多少、什么条件下才真的值得换,很少有人给出具体数字。真实计时对比两者在不同并发强度下的表现。

0.8~0.9 倍
真实测出:单线程无竞争时,LongAdder 反而比 AtomicLong 略慢,不是任何场景下都更快
40~55 倍
真实测出:16 线程高竞争下,LongAdder 真实快出 40 到 55 倍,两次独立运行结果一致

上一篇:CopyOnWriteArrayList:真实验证迭代时改了也不报错,但代价随长度真实涨

思路:三种并发强度,同样的自增总量

1 线程(无竞争)、4 线程、16 线程,分别用 AtomicLong.incrementAndGet() 和 LongAdder.increment() 各做同样总量的自增,真实计时对比。

LongAdderVsAtomic.java(核心对比逻辑) // AtomicLong:所有线程真实竞争同一个 CAS 目标 for (int j = 0; j < perThread; j++) counter.incrementAndGet(); // LongAdder:内部真实分散成多个 Cell,不同线程大概率落在不同 Cell 上 for (int j = 0; j < perThread; j++) counter.increment();

真实验证:竞争越激烈,差距越大

每种并发强度下,每个线程真实做 200 万次自增,总数用 counter.get()/sum() 真实核对无误后再比较耗时。

真实实测 · 第一次运行
1 线程, 每线程 2000000 次自增:
  AtomicLong 耗时 = 9ms
  LongAdder  耗时 = 10ms
  倍数(AtomicLong/LongAdder) = 0.90

4 线程, 每线程 2000000 次自增:
  AtomicLong 耗时 = 193ms
  LongAdder  耗时 = 11ms
  倍数(AtomicLong/LongAdder) = 17.55

16 线程, 每线程 2000000 次自增:
  AtomicLong 耗时 = 1768ms
  LongAdder  耗时 = 44ms
  倍数(AtomicLong/LongAdder) = 40.18
真实实测 · 第二次独立运行
1 线程: AtomicLong=8ms, LongAdder=10ms, 倍数=0.80
4 线程: AtomicLong=215ms, LongAdder=10ms, 倍数=21.50
16 线程: AtomicLong=1657ms, LongAdder=30ms, 倍数=55.23

两次运行趋势完全一致:单线程时 LongAdder 反而略慢(0.8~0.9 倍),4 线程时快 17~21 倍,16 线程时快 40~55 倍——真实数据呈现出随并发强度增加、优势指数级放大的曲线。原因在内部实现:AtomicLong 所有线程真实竞争同一个 volatile long 值上的 CAS,线程越多,CAS 失败重试的真实次数越多;LongAdder 内部有一个 base 加一组 Cell[] 数组,不同线程按线程探针哈希大概率落到不同的 Cell 上各自累加,真实分散了竞争,只有调用 sum() 读取总数时才需要把所有 Cell 真实加起来。

为什么单线程反而慢?无竞争时,AtomicLong 的单次 CAS 几乎总能一次成功,路径很短;LongAdder 即使只有一个线程,内部逻辑也要多走一层判断(要不要初始化 Cell[]、走哪个 Cell),这点额外开销在完全没有竞争、CAS 从不失败的场景下反而成了净负担。真实结论是:LongAdder 不是"更快的 AtomicLong",而是"用空间和读取时的额外累加,换高竞争下的真实写入吞吐量"——只在真的存在高并发写竞争时才值得换。

分步演示:LongAdder 内部结构是怎么真实随竞争程度变化的

每一行都是真实反射或真实运行这段代码得到的结果。

LongAdder真实只在没有竞争时才用单个base字段;真实竞争一旦发生,才会自动展开成多个Cell分摊CAS压力——这一切对外的sum()结果真实始终精确
这一步新验证的内部结构

    参考与说明

    • 本文全部计时数据均在本机真实运行 Java 17、8 核并发得到,两次独立运行倍数量级一致(个位数差异属于正常波动)。
    • 和synchronized 锁膨胀那篇是同一个主题的不同角度:都是"竞争越激烈,某种设计的代价越明显"。
    • 没有涉及:Cell[] 内部的扩容策略、LongAccumulator(比 LongAdder 更通用,支持自定义累加函数)——这些留给系列后面按需再写。
    京ICP备2026067502号-1京公网安备11010502063487号