longadder通过分段cas(cell数组)缓解单点竞争,采用base+cells两级结构、懒初始化、失败后重哈希换cell、扩容机制及无锁sum(),显著提升高并发写吞吐。

Java 中 CAS 本身是硬件级原子指令,但单点竞争会让它成为性能瓶颈。LongAdder 不是抛弃 CAS,而是用“分段 CAS”把原本挤在同一个内存地址上的所有线程,分散到多个独立 Cell 上各自执行 CAS,从而缓解总线争抢和缓存行伪共享,显著提升高并发写吞吐。
把单点竞争拆成多点低烈度并发
CAS 的瓶颈不在指令本身,而在于多核 CPU 同时更新同一缓存行时触发 MESI 协议频繁同步,甚至总线锁定。LongAdder 的 Cell[] 数组每个元素都是独立对象,JVM 分配不同内存地址(还配合 @Contended 防伪共享),让线程不再争抢 valueOffset 这一个偏移量,而是各自在不同 Cell.value 上做 CAS。
- 100 个线程争 1 个地址 → 大量失败、自旋、CPU 空转
- 100 个线程平均分到 10 个 Cell → 每个 Cell 约 10 线程竞争,CAS 成功率大幅提升
- 底层 unsafe.compareAndSwapLong 操作的是不同对象的不同字段偏移,物理上不共享缓存行
base + cells 两级结构,懒初始化降低初始开销
LongAdder 并不一上来就建数组,而是先走轻量路径:低并发时只用 base 字段,直接 CAS 更新;只有检测到竞争(比如 CAS 失败),才初始化 cells 数组并开始分流。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 首次调用 add() 且无竞争 → 成功更新 base,零额外开销
- 第二次 CAS 失败 → 初始化 cells = new Cell[2],后续哈希定位到某个 cell 执行 CAS
- 持续高竞争 → cells 按 2 的幂次扩容(2→4→8…),动态稀释单点压力
失败后主动换位置,不是死等一个地址
AtomicLong 在 CAS 失败后只能原地重试同一地址,导致大量无效自旋。LongAdder 则不同:线程在某个 Cell 上失败后,会调用 advanceProbe() 更新探针值,重新哈希找下一个 Cell;如果所有 Cell 都忙,才退回到 base 尝试一次,再考虑扩容。
- 本质是从“等待成功”转向“寻找空闲”,用空间冗余换时间确定性
- 扩容由首个发现严重竞争的线程触发,其他线程随后感知并参与分流
- 避免了线程长时间卡在单一内存地址上空转
sum() 设计为纯读操作,不参与写竞争
LongAdder 的 sum() 方法只是遍历 cells 数组累加各 Cell.value,再加 base 值。它不修改任何状态,也不参与 CAS 竞争,因此不会加剧写热点。
- 读操作完全无锁,开销极低
- 适合写多读少场景(如 QPS 统计、请求计数)
- 注意:sum() 返回的是最终一致值,不是严格原子快照
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










