ConcurrentHashMap是怎么记录元素个数size的?

HashMap默认是非线程安全的,可以认为每次只有一个线程来执行操作,所以hashMap就使用一个很简单的int类型的size变量来记录HashMap键值对数量就行了。 HashMap记录键值对数量的实现如下: ```java transient int size; public int size() { return size; } ``` ConcurrentHashMap记录键值对数量的实现如下: ```java //size方法最大只能返回Integer.MAX_VALUE public int size() { long n = sumCount(); return ((n < 0L) ? 0 : (n > (long)Integer.MAX_VALUE) ?Integer.MAX_VALUE : (int)n); } //mappingCount方法可以返回long类型的最大值, public long mappingCount() { long n = sumCount(); return (n < 0L) ? 0L : n; // ignore transient negative values } private transient volatile long baseCount; private transient volatile CounterCell[] counterCells; //sumCount会返回sumCount加上CounterCells数组中每个元素as存储的value 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; } @sun.misc.Contended //这个注解可以避免伪共享,提升性能。加与不加,性能差距达到了 5 倍。在缓存系统中,由于一个缓存行是出于32-256个字节之间,常见的缓存行为64个字节。而一般的变量可能达不到那么多字节,所以会出现多个相互独立的变量存储在一个缓存行中的情况,此时即便多线程访问缓存行上相互独立变量时,也涉及到并发竞争,会有性能开销,加了@sun.misc.Contended这个注解,在jDK8中,会对对象前后都增加128字节的padding,使用2倍于大多数硬件缓存行的大小来避免相邻扇区预取导致的伪共享冲突。 static final class CounterCell { volatile long value; CounterCell(long x) { value = x; } } ``` 每次添加x个新的键值对后,会调用addCount()方法使用CAS操作对baseCount+x,如果操作失败,那么会新建一个CounterCell类型的对象,保存新增的数量x,并且将对象添加到CounterCells数组中去。