longadder 通过 cell 数组分散 cas 争用、哈希分流+probe 动态重试避免死磕、两级懒初始化平衡开销与扩展性、sum() 无锁读实现读写隔离,显著提升高并发写性能。

Java 中 CAS 机制本身无法消除多核冲突,它只是保证单地址操作的原子性;真正减少冲突的是 LongAdder 的 Cell 数组结构设计——把原本挤在同一个内存地址上的所有线程,分散到多个彼此隔离的地址上执行 CAS。
Cell 数组让 CAS 落在不同缓存行,从物理层面切断争用
CAS 指令(如 cmpxchg)在硬件层依赖总线或缓存一致性协议(如 MESI),当大量线程反复对同一内存地址执行 CAS,就会引发频繁的缓存行失效与总线争抢。LongAdder 的 Cell[] 数组中每个 Cell 是独立对象,JVM 为其分配不同起始地址,并通过 @Contended 注解强制填充,确保每个 Cell.value 独占一个 64 字节缓存行。这意味着:
- Thread-0 更新
cells[0].value,和 Thread-1 更新cells[1].value,操作的是完全不重叠的缓存行 - 即使哈希映射到相邻槽位,也不会触发伪共享(False Sharing)
- 硬件级争用从“100 个线程抢 1 个地址”降为“平均 10 个线程抢 1 个地址”
哈希分流 + probe 动态重试,避免线程死磕单点
LongAdder 不靠固定绑定,而是用线程本地的 probe 值做哈希定位:
- 首次尝试更新 base,失败则根据
ThreadLocalRandom.getProbe()计算哈希,映射到cells[i] - 若该 Cell 正被其他线程更新导致 CAS 失败,线程立即调用
advanceProbe()更新 probe,重新哈希找下一个空闲 Cell - 不是无限重试同一个位置,而是主动“换地方”,把时间压力转为空间冗余
两级结构 + 懒初始化,兼顾低并发轻量与高并发可扩展
LongAdder 并不一上来就建数组,而是按需演进:
- 无竞争时:所有线程都走
base的 CAS,开销最小 - 首次竞争失败:触发
cells = new Cell[2],开始分流 - 持续高竞争:首个检测到严重冲突的线程负责扩容(2→4→8…),其他线程随后自动参与新槽位更新
- 这种“先集中、再分散”的策略,既避免小流量浪费资源,又防止大流量压垮单点
sum() 不参与写路径,彻底解除读写干扰
LongAdder 的读操作 sum() 是纯遍历:累加 base + cells[i].value 所有值,不加锁、不 CAS、不修改任何状态。这带来两个关键好处:
- 读操作不会阻塞写,写操作也不因读而退避
- 写路径全程只涉及 volatile 写或 CAS,没有全局同步点,各 Cell 更新完全异步
- 适合写多读少场景——写性能随核数近似线性提升,读只需一次快照汇总
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











