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

 

 

 

 posted on 2020-09-24 11:20  胡萝卜钓鱼  阅读(91)  评论(0)    收藏  举报