ConcurrentHashMap 的 size() 返回的是基于 baseCount 和 CounterCell 数组的快照值,并非实时精确值;它通过懒更新和分散计数减少竞争,但扩容、CAS 失败等场景会导致计数延迟,故不保证强一致性。

ConcurrentHashMap 的 size() 方法并不直接遍历所有桶(bin),而是通过内部维护的 `sumCount` 字段来估算当前元素总数,但这个值不是实时精确的——它本质上是多个分段计数器(CounterCell 数组)的累加和,加上 baseCount 的值。
sumCount 是怎么来的?
ConcurrentHashMap 使用一种“懒更新 + 分散计数”的策略来避免高并发下对全局计数器的激烈竞争:
- 每次
put、remove成功后,会尝试调用addCount(1L, binCount)更新计数; -
addCount先尝试原子更新baseCount;失败则进入竞争路径,尝试初始化或更新CounterCell[]数组中的某个槽位; -
CounterCell是一个简单的 long 包装类,每个线程哈希到不同槽位,减少写冲突; -
sumCount()就是把baseCount和所有非 null 的CounterCell.value加起来。
为什么 size() 不保证强一致性?
因为计数更新是异步且尽力而为的:
- 某些操作(如扩容中的迁移、CAS 失败重试)可能暂时未计入
sumCount; -
size()调用时不会阻塞写操作,也不会强制刷新所有 pending 计数; - JDK 9+ 的文档明确指出:“The value returned is a snapshot and may not reflect unprocessed updates.”
实际调用流程简述
当你调用 size() 时,它内部执行:
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。 当用户需要:(1) 编写或审查Java代码 (2) 检查命名/代码规范 (3) 处理异常和日志 (4) 编写单元测试 (5) 安全编码 (6) 数据库设...
立即学习“Java免费学习笔记(深入)”;
public int size() {
long n = sumCount();
return ((n < 0L) ? 0 :
(n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
(int)n);
}
其中 sumCount() 实现类似:
final long sumCount() {
CounterCell[] as = counterCells; long sum = baseCount;
if (as != null) {
for (CounterCell a : as)
if (a != null)
sum += a.value;
}
return sum;
}
如果需要更准确的计数怎么办?
没有银弹,但可根据场景选择:
- 日常监控或粗略判断:直接用
size()完全够用; - 测试或断言要求严格一致:可配合
mappingCount()(返回long,语义同size(),但不截断)并接受其仍是快照; - 真正需要精确实时总数(极少见):只能自己加锁遍历,或改用其他支持强一致计数的数据结构(如
CopyOnWriteArrayList配合额外计数器,但性能代价巨大)。

















