对于ConcurrentHashMap而言,需要保证的是任何操作的线程安全,包括对集合元素个数的统计。
一般在多线程下要统计一个全局数量大小,可以通过cas+循环(或者直接用Atomic相关的类)的方式实现,但是ConcurrentHashMap作者却并不是这样实现的。
先看看如何获取集合中的元素个数,通过调用size()方法:
public int size() {
long n = sumCount();
return ((n < 0L) ? 0 :
(n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
(int)n);
}
ConcurrentHashMap获取整个集合的元素个数利用的是sumCount()方法来进行计算,在普通的HashMap中就是直接返回一个全局的size值。
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;
}
从sumCount方法中可以看出,结果总数来源于两部分,第一部分:全局baseCount变量值,这个值作用和size是差不多的;第二部分:CounterCell数组中的各个value。
ConcurrentHashMap的作者希望通过CounterCell数组来减少多线程环境下cas自旋所造成的损耗。(这个想法真的可以好好学习下🤔)
其实,CounterCell对象内部就只有一个用volatile
修饰的value。
@sun.misc.Contended static final class CounterCell {
volatile long value;
CounterCell(long x) {
value = x; }
}
接下去就深入看下ConcurrentHashMap是如何来实现计数的了。。。。
**addCount()**方法会在调用put()之类的方法后,如果是新增了节点就会执行该方法,x一般都是1。
private final void addCount(long x, int check) {
CounterCell[] as; long b, s;
// 当计数盒子为空的时候,直接修改baseCount
// 如果修改baseCount失败,将值保存到计数盒子
if ((as = counterCells) != null ||
!U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
CounterCell a; long v; int m;
// cas操作是否成功,默认true
boolean uncontended