LongAdder 内部分段原理
                           
天天向上
发布: 2025-07-13 11:57:24

原创
793 人浏览过

LongAdder 是 Java 8 引入的并发计数类,是 java.util.concurrent.atomic 包的一部分,用于在 高并发场景下替代 AtomicLong,显著提高性能。


一、LongAdder 是什么?

定义:

LongAdder 是一种高并发场景下更高效的累加器,通过**分段计数(striped cells)**来减少线程争用,最终通过将多个值相加得出总结果。


二、为什么不用 AtomicLong

AtomicLong 的缺陷:

  • 基于 CAS(Compare-And-Swap) 实现;
  • 所有线程竞争同一个值(单点),导致严重的 伪共享竞争冲突
  • 在高并发场景中,会频繁自旋失败、性能下降。

三、LongAdder 的分段原理核心思想

多个线程并发写入不同的槽(Cell),有效降低冲突,最后通过累加所有槽的值得到总和。


四、底层核心结构

class LongAdder {
    // 用于低并发下直接累加
    transient volatile long base;

    // 高并发时使用的分段数组(长度是2的幂次)
    transient volatile Cell[] cells;

    // 用于线程定位Cell数组槽位的探针变量
    transient final ThreadLocalRandomProbe probe;
}

核心组件详解:

组件含义
base单线程或低并发时使用,类似 AtomicLong 的值
cells[]分段数组,高并发时每个线程命中一个 Cell,减少冲突
Cell内部类,每个 Cell 维护一个 long 值,并通过 CAS 操作

五、工作原理(增量流程)

方法:LongAdder.increment() / add(long x)

执行流程:

  1. 线程尝试对 base 做一次 CAS 增加操作;
  2. 如果竞争不激烈(低并发),操作成功,返回;
  3. 如果竞争激烈(CAS失败):
  • 初始化 cells[] 数组;
  • 将线程映射到一个 Cell 槽位(根据 thread probe 值);
  • Cell.valueCAS 累加;
  • 如果冲突严重,可能扩容 cells(最大可扩至 CPU 核心数 × 2);

关键点:

  • 每个线程尽量只操作一个 Cell,互不干扰
  • 最终通过 sum()base + 所有 cell 值相加 得到总和。

六、源码精要解析(简化)

1. add(long x) 方法片段:

public void add(long x) {
    Cell[] as; long b, v; int m; Cell a;

    // 尝试直接更新 base
    if ((as = cells) == null || !casBase(b = base, b + x)) {
        boolean uncontended = true;
        if (as == null || (m = as.length - 1) < 0 ||
            (a = as[getProbe() & m]) == null ||
            !(uncontended = a.cas(v = a.value, v + x))) {
            longAccumulate(x, null, uncontended);
        }
    }
}

2. sum() 方法:

public long sum() {
    Cell[] as = cells;
    long sum = base;
    if (as != null) {
        for (Cell a : as) {
            if (a != null)
                sum += a.value;
        }
    }
    return sum;
}

七、性能对比(JMH 测试)

测试场景(1000 线程)AtomicLong 耗时LongAdder 耗时性能提升比例
1 亿次累加4000ms300ms约 13 倍

来源:JMH 基准测试


八、LongAdder 的优缺点

优点说明
极高并发性能分段避免竞争冲突
减少伪共享每个线程写入独立槽位,避免 cache line 竞争
CAS 成功率高分摊压力至多个 Cell
缺点说明
不能保证强一致性累加过程非原子(多个 Cell),如用于唯一 ID 生成不适合
获取总值需要 sum() 扫描数组成本高于原子值,但适用于大多数统计场景

九、实际使用场景

场景说明
网站访问量统计比如 PV / UV 计数,性能优于 AtomicLong
日志计数器 / 异常统计统计某类异常、接口调用次数等高频场景
指标收集系统(如 Prometheus)高并发下的监控指标累加

十、权威链接与文档


十一、总结答题结构(面试可用)

问题回答建议
什么是 LongAdder?高并发下分段累加,替代 AtomicLong,避免竞争
它怎么提升性能?使用 Cell[] 分段,线程写入各自槽位,减少 CAS 冲突
与 AtomicLong 的区别?AtomicLong 单点竞争,LongAdder 分散压力,性能提升巨大
有什么缺点?获取总值需合并所有 Cell,非强一致性,不适合计唯一值
哪些场景适合?并发统计、计数器、接口监控、性能日志等高频加法场景

更多详细内容请关注其他相关文章!

发表回复 0

Your email address will not be published. Required fields are marked *