longadder通过base+cell[]分段cas实现高并发计数优化,写吞吐显著提升但读结果可能滞后;适用于写多读少、允许误差的场景,不支持原子组合操作。

LongAdder 通过“分段 CAS”把单点竞争拆成多路并行更新,显著降低高并发下的计数开销。它不靠一个变量硬扛所有线程,而是用 base + Cell[] 的结构,让线程尽量各自更新不同内存位置,减少 CAS 失败和 CPU 空转。
核心机制:base 和 Cell[] 协同分流
LongAdder 内部维护两个关键部分:
- base:一个 volatile long 值,无竞争时所有线程直接对它做 CAS 更新(行为类似 AtomicLong)
- Cell[] 数组:延迟初始化的分片单元数组,每个 Cell 包含独立的 volatile long value;线程根据哈希值定位到某个 Cell,只在该 Cell 上尝试 CAS
当多个线程同时调用 add(1),它们大概率被分配到不同 Cell,彼此不干扰——相当于把“抢一个收银台”变成“分散到多个窗口排队”。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
Cell 内部仍用 CAS,但冲突大幅下降
每个 Cell 的更新逻辑仍是基于 Unsafe.compareAndSwapLong 的 CAS 操作,但关键区别在于:
- 竞争范围从“全局唯一地址”缩小到“单个 Cell 的 value 字段”
- 若当前 Cell CAS 失败,线程不会死等重试,而是尝试换一个 Cell 继续 CAS(rehash 后再找)
- Cell 类加了
@Contended注解,每个 Cell 占满 64 字节缓存行,避免多个 Cell 被塞进同一缓存行导致伪共享
读操作不保证强一致,但换来了写吞吐提升
sum() 方法只是简单遍历所有 Cell 并累加 base 值,期间 Cell 值可能还在被其他线程更新。这意味着:
- 读不是快照,结果可能略滞后(比如刚 increment 的值还没被 sum 捕获)
- 但写路径极轻量:多数情况下只需一次 CAS 就完成,无需锁或复杂同步
- 实测在 256 线程场景下,吞吐比 AtomicLong 高出 20 倍以上
适用边界要清楚:不是万能替代
LongAdder 不适合所有计数场景,需满足几个前提:
- 写远多于读(如每秒百万次 increment,几分钟才查一次 sum)
- 允许短暂统计误差(监控指标、限流阈值、日志打点都符合)
- 不需要 compareAndSet、getAndIncrement 等原子组合操作(它只提供 add / increment / sum / reset)
- 不追求内存占用最小化(Cell[] 默认初始大小为 2,最大可扩容至 CPU 核心数级别)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










