跳过导航

LongAdder 为什么比 AtomicLong 更适合高竞争场景?

约 4 分钟...次浏览
专栏Java 并发编程第 4 篇

AtomicLongLongAdder 都能避免计数丢失,却提供不同语义。前者维护一个可线性化的值;后者把写竞争分散到多个槽位,读取时汇总,换取高并发吞吐。

AtomicLong 的热点 CAS

for (;;) {
    long current = value;
    long next = current + 1;
    if (compareAndSet(current, next)) return next;
}

低竞争时 CAS 非常高效。线程增多后,所有核心争抢同一缓存行:一个核心成功写入会使其他核心缓存中的副本失效,失败线程重新读取并重试。CPU 没有进入内核阻塞,但一致性流量和空转仍然昂贵,所以“无锁”不等于“无竞争”。

LongAdder 如何分散写入

LongAdder 基于 Striped64,内部包含 base 和一组 Cell。无竞争时更新 base;发生冲突后,根据线程探针选择 Cell,冲突持续时还可能扩容。最终结果为:

long sum = base;
for (Cell cell : cells) {
    if (cell != null) sum += cell.value;
}

多个线程大概率写不同缓存行,减少 CAS 冲突。Cell 还通过填充降低伪共享风险——逻辑上互不相关的变量若落在同一缓存行,也会因彼此写入反复失效。

必须接受的语义代价

sum() 在遍历期间其他线程仍可更新,所以它不是某一瞬间的原子快照。sumThenReset() 也不适合与并发更新配合实现“绝不遗漏”的财务结算。

因此如下场景应使用 AtomicLong:生成严格唯一递增序号、CAS 状态机、读取结果决定后续原子操作。如下场景适合 LongAdder:请求次数、命中次数、错误数等高频指标,允许采集瞬间存在轻微偏差。

class Metrics {
    private final LongAdder requests = new LongAdder();
    void record() { requests.increment(); }
    long requestCount() { return requests.sum(); }
}

如果需要 incrementAndGet() 的返回值参与业务,LongAdder 没有等价方法,这不是 API 遗漏,而是其设计无法低成本提供全局线性化结果。

一份可复现的 JMH 基准

@State(Scope.Benchmark)
public class CounterBench {
    AtomicLong atomic = new AtomicLong();
    LongAdder adder = new LongAdder();

    @Benchmark @Threads(16)
    public long atomic() { return atomic.incrementAndGet(); }

    @Benchmark @Threads(16)
    public void adder() { adder.increment(); }
}

两个 benchmark 返回语义不同,若只比较写吞吐要避免频繁调用 sum();另设读写混合组更贴近指标采集。测试 1、2、CPU 核数、2 倍核数等线程档位,并报告 JDK、CPU 和 fork 参数。低竞争时 LongAdder 未必更快,还占用更多内存。

常见误用

// 错误:不是原子限流
adder.increment();
if (adder.sum() > limit) reject();

多个线程可能同时越过阈值。限流需要定义窗口和原子许可获取,可考虑 Semaphore、令牌桶或集中式原子脚本。另一个误用是为每个高基数标签创建 LongAdder,可能让指标系统因 Cell 数组和标签组合消耗大量内存。

生产检查清单

  • 是否真的存在 CAS 热点?先用压测和性能事件证明。
  • 读取是否要求精确、原子且能驱动业务决策?
  • 是否需要每次递增后的唯一返回值?
  • 指标标签基数是否受控?
  • 是否错误地用 sumThenReset() 保证无损分段结算?
  • 是否同时评估内存占用与读操作成本?

选择计数器时先写下语义,再谈性能。可以近似读取的高频统计用 LongAdder,必须线性化的状态仍应使用 AtomicLong 或更完整的同步方案。

分享:
文章作者:狼码纪
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。文章可能参考了其他优秀文章,如有侵权请联系删除。