-
遍历 Segment:在 Java 7 及之前的版本中,ConcurrentHashMap 是基于分段锁(Segment)实现的。每个 Segment 独立地维护了一部分映射的元素,并且有自己的锁。调用 size()方法时,会遍历所有的 Segment。
-
累加计数器:每个 Segment 维护了一个 count 变量,表示该 Segment 的元素数量。size()方法将通过遍历所有 Segment,累加这些 count 变量来得到总大小。 -
重试机制:因为其他线程可能正在并发修改 ConcurrentHashMap,所以 size 计算过程中可能会出现不一致。为了解决这个问题,初次计算后,如果检测到结构性修改,会重新尝试获取大小。 -
返回大小:如果没有检测到并发修改,则返回累加后的大小;如果在尝试了若干次(通常是三次)后仍然检测到并发修改,则放弃并返回当前累加值。
Java 8 及之后的版本
public int size() {
long n = sumCount();
return ((n < 0L) ? 0 : (n > (long) Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) n);
}
final long sumCount() {
CounterCell[] as = counterCells;
CounterCell a;
long sum = baseCount;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null) sum += a.value;
}
}
return sum;
}
在这个代码中:
-
baseCount 是一个长整型变量,用于在没有竞争时快速统计。
-
counterCells 是一个 CounterCell 数组,每个 cell 包含一个值,在多线程环境下,每个线程可以操作数组中不同的 cell 来减少争用。 -
sumCount()方法遍历所有的 cells 来累加计数,加上 baseCount 以得到总的元素数。
准确性
size()方法返回的结果通常是近似值,而不是绝对准确的:
-
并发更新:如果在执行size()方法的同时有其他线程在修改ConcurrentHashMap,则返回的大小可能与实际大小不符。
-
最终一致性:尽管存在并发更新问题,但在没有线程活动修改映射时调用size(),或者在所有线程都看到映射状态一致时调用size(),它将返回准确的大小。 -
越界检查:size()方法会检查计算出的大小,如果超过Integer.MAX_VALUE,则返回Integer.MAX_VALUE。这意味着极大的ConcurrentHashMap实例的大小将被截断。
本篇文章来源于微信公众号: 互联网面试小帮手
微信扫描下方的二维码阅读本文

Comments NOTHING