ConcurrentHashMap用LongAdder实现计数,核心是“分而治之”:通过base值与可扩容Cell数组分散更新压力,避免AtomicLong单点CAS竞争,保障高并发下吞吐量与最终一致性。

ConcurrentHashMap 用 LongAdder 实现元素个数统计,核心是“分而治之”——不靠单个变量硬扛所有线程更新,而是把计数任务分散到多个独立单元中执行,最后汇总。这种设计直击高并发下 CAS 竞争的痛点,不是追求每次操作都立刻可见总数,而是保障最终结果准确、中间过程高效。
为什么不用 AtomicLong?
AtomicLong 依赖单一变量 + CAS 自旋。当大量线程同时调用 addCount(1) 时,它们都在抢同一个 baseCount 的更新权。失败线程必须重读、重算、再试,造成:
- CPU 空转严重,尤其在多核高负载场景
- 吞吐量随线程数增长迅速下降,出现明显性能拐点
- 计数延迟不可控,
size()返回值可能滞后于实际插入
LongAdder 是怎么分摊压力的?
它内部维护一个基础值 base 和一个可动态扩容的 Cell[] 数组(每个 Cell 就是一个带 volatile long value 的独立计数格子):
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 初始无竞争:所有线程都尝试 CAS 更新
base,轻量高效 - 检测到竞争(CAS 失败):当前线程随机选取一个
Cell(通过ThreadLocalRandom.getProbe() & (cells.length-1)定位),对该格子做本地 CAS 更新 - 数组扩容:若某格子持续争抢,会触发扩容(上限通常为 CPU 核心数),让更多线程分流到新格子
ConcurrentHashMap 中的计数流程
每次成功插入或删除元素后,都会调用 addCount(x, check):
立即学习“Java免费学习笔记(深入)”;
- 先尝试快速路径:对
baseCount执行一次 CAS 加减 - 失败则进入竞争路径:定位并更新所属
CounterCell;若格子为空或再次冲突,则调用fullAddCount协助初始化或扩容 -
size()方法返回的是baseCount + sum(cells),即所有格子值的实时总和
这种思想的本质是什么?
它放弃“强一致性读”的幻觉,换来了可伸缩性:
- 不保证任意时刻都能读到最新总数,但保证最终一致
- 用少量内存(
Cell数组)换取显著降低的写竞争 - 天然适配现代多核 CPU 架构——每个核心可独立更新不同
Cell,避免缓存行伪共享(@Contended注解已用于优化)

















