LongAdder采用分段累加机制:低并发时直接CAS更新base,零成本;首次竞争时懒加载cells数组;高并发下自动扩容并绑定线程;@Contended注解避免伪共享。

base 是低并发时的“单点入口”
LongAdder 初始化后,cells 数组为 null,所有累加操作都直接尝试 CAS 更新 base 字段。这时候行为和 AtomicLong 几乎一致:轻量、无额外内存开销、一次 CAS 成功即完成。
只要没有线程因竞争 base 而失败,LongAdder 就一直停留在这个状态。它不预分配 cells,也不做任何哈希计算——真正做到了“无争抢,零成本”。
cells 数组在首次竞争时懒加载
当某个线程调用 add() 时 CAS base 失败(说明已有其他线程正在更新),就会触发初始化逻辑:
- 先尝试用自旋锁
cellsBusy占位(CAS 设置为 1) - 成功后创建长度为 2 的
Cell[]数组 - 把原 base 值按某种策略(如均分或保留)迁移过去,然后重置 base 为 0(部分实现中 base 不清零,仅作为 fallback)
- 后续线程不再猛攻 base,而是转向 cells 数组
高并发下 cells 自动扩容与线程绑定
cells 不是固定大小。当线程通过哈希定位到某个 cell(getProbe() & (cells.length - 1))并发现该位置为空,或 CAS 失败次数过多,就会触发扩容:
- 扩容前再次检查
cellsBusy,确保串行化操作 - 新数组长度为原长的 2 倍(保持 2 的幂,便于位运算取模)
- 扩容上限通常与 CPU 核心数相关——超过后继续扩容收益递减,JDK 默认最多到 CPU 数的 2 倍左右
- 每个线程的 probe 值由 ThreadLocalRandom 维护,基本稳定,所以同一线程大概率持续写入同一个 cell,减少跨核缓存同步
@Contended 解决伪共享,让每个 cell 独占缓存行
cells 是连续数组,若不加防护,多个 Cell 对象可能落在同一 CPU 缓存行(64 字节)。一个线程修改 cell[0],会导致另一个线程的 cell[1] 所在缓存行失效(false sharing),白白刷缓存。
Cell 类上标注了 @Contended 注解:
- JVM 在对象布局时自动填充空白字节,确保每个 Cell 至少独占一个缓存行
- 该优化对多核 NUMA 架构尤其关键,避免跨 socket 缓存同步开销
- 需开启 JVM 参数
-XX:+UseContended(JDK 8u20+ 默认启用)才生效










