jdk1.7中采用Segment
+ HashEntry
的方式进行实现,结构如下:
理解为将原本HashMap中的数组进行分段,每个段加锁,修改的时候必须先获得所在段的锁,相比于Hashtable(已废弃,不建议使用)提高了并发度;
在计算总数量的时候会存在问题:
因为ConcurrentHashMap
是可以并发插入数据的,所以在准确计算元素时存在一定的难度,一般的思路是统计每个Segment
对象中的元素个数,然后进行累加,但是这种方式计算出来的结果并不一样的准确的,因为在计算后面几个Segment
的元素个数时,已经计算过的Segment
同时可能有数据的插入或则删除,在1.7的实现中,采用了如下方式:
先采用不加锁的方式,连续计算元素的个数,最多计算3次:
1、如果前后两次计算结果相同,则说明计算出来的元素个数是准确的;
2、如果前后两次计算结果都不同,则给每个
Segment
进行加锁,再计算一次元素的个数;
1.8中放弃了Segment
臃肿的设计,取而代之的是采用Node
+ CAS
+ Synchronized
来保证并发安全进行实现,结构如下:
在向null的桶put时,用CAS方法直接put,不加锁,失败则用synchronized加锁,如果正在扩容,那么先扩容
else if ((fh = f.hash) == MOVED) tab = helpTransfer(tab, f);
统计size
ConcurrentHashMap的元素个数等于baseCounter和数组里每个CounterCell的值之和,这样做的原因是,当多个线程同时执行CAS修改baseCount值,失败的线程会将值放到CounterCell中。所以统计元素个数时,要把baseCount和counterCells数组都考虑。
参考
https://blog.csdn.net/ym123456677/article/details/78860719
https://blog.csdn.net/programmer_at/article/details/79715177