LongAdder通过分段累加Cells数组降低线程竞争,以空间换时间提升高并发吞吐量;它采用懒初始化、动态扩容与probe哈希定位,提供bounded误差的最终一致性sum(),适用于非强一致性的统计场景。

LongAdder 通过分段累加 Cells 数组,把高竞争的单点 CAS 操作,拆成多个低竞争的局部更新,显著降低线程冲突概率。它不追求强一致性(如 get() 不实时反映所有更新),而是用空间换时间,在高并发累加场景下获得远超 AtomicLong 的吞吐量。
Cells 数组实现“分段”:每个线程尽量操作自己的 Cell
LongAdder 内部维护一个 cells 数组,每个 Cell 是一个 volatile long 类型的容器。当线程调用 add() 时:
- 先尝试更新 base 字段(类似 AtomicLong 的 value),失败则进入分段逻辑
- 根据当前线程的 probe 值(由 ThreadLocalRandom.getProbe() 生成)计算 hash 索引,定位到某个 cell
- 若该 cell 存在且 CAS 成功,累加完成;否则触发扩容或重试
- probe 值随线程运行动态变化,有助于避免哈希冲突集中
动态扩容与懒初始化:按需分配资源,避免空耗
Cells 数组不是一开始就分配好,而是懒初始化:
- 初始为 null,首次竞争失败后才创建长度为 2 的数组
- 当多个线程频繁 hash 到同一 cell 并 CAS 失败时,触发扩容(长度翻倍)
- 扩容前会先尝试帮助其他线程完成 cell 初始化,减少争抢
- 扩容过程加锁(通过 cellsBusy 字段 CAS 控制),但只在扩容瞬间阻塞,不影响日常更新
get() 是最终一致性:sum() 需遍历 base + 所有非空 cell
LongAdder 放弃了实时精确性来换取性能:
立即学习“Java免费学习笔记(深入)”;
- add() 操作只更新 base 或某个 cell,不保证立即可见全局
- get() 方法实际调用 sum(),遍历 base 加上所有非 null cell 的 value
- sum() 不加锁,可能看到部分 cell 的旧值,但误差 bounded(通常极小)
- 如果需要严格一致的值(如计费场景),应避免用 LongAdder,改用 AtomicLong 或同步控制
适用场景明确:高并发累加,非强一致性要求
LongAdder 不是 AtomicLong 的通用替代品:
- 适合统计类场景:QPS 计数、请求总量、耗时累加等,允许短暂延迟和微小误差
- 不适合需要 compareAndSet、getAndIncrement 或严格顺序语义的逻辑
- 内存开销略大(cells 数组 + 多个 Cell 对象),但现代服务器通常可接受
- 单线程或低并发下,AtomicLong 反而更快(无 hash、无数组访问开销)


















