什么是ConcurrentHashMap--抄自小灰

hashmap不是线程安全的,在并发插入元素的时候,有可能出现带环链表,让下一次读操作出现死循环。
想要避免hashmap的线程安全问题有很多办法,比如改用hashtable或者collections.synchronizedMap。但是这两者都有一个问题,就是性能。无论是读操作还是写操作,它们都会给整个集合加锁,导致同一时间的其他操作阻塞。如下图所示:
在这里插入图片描述
为了在并发环境下,能兼顾线程安全和运行效率,ConcurrentHashmap就应运而生了。

1、ConcurrentHahMap
ConcurrentHashMap其实很简单,最关键是要理解一个概念(segment)
segment是什么呢?就相当于一个hashMap对象,包含一个hashEntry数组,数组中的每一个hashEntry既是一个键值对,也是一个链表的头节点。其结构如下图所示:
在这里插入图片描述
像这样的segment对象,在concurrentHashMap集合中有多少个?有2的n次方个,共同保存在一个名为segments的数组当中。因此整个concurrentHashMap的结构如下:
在这里插入图片描述
可以说,concurrentHashMap是一个二级哈希表,在一个总的哈希表下面,有若干个子哈希表。
其实这被称为“锁分段技术”,每一个segment就好比一个自治区,读写操作高度自治,segment之间互不影响。

情形1:不同segment的并发写入,此时是可以并发执行的
在这里插入图片描述
情形2:同一个segment的读写
在这里插入图片描述
情形三:同一segment的并发写入
在这里插入图片描述
也就是说,segment的写入是需要上锁的,由此可见,concurrentHashMap当中每个segment各自持有一把锁,在保证线程安全的同时降低了锁的粒度,让并发操作效率更高。

2、concurrentHashMap的读写过程:
put方法:
a、为输入的key做hash运算,得到hash值
b、通过hash值,定位到对应的segment对象
c、获取可重入锁
d、再次通过hash值,定位到segment当前数组的具体位置
e、插入或覆盖hashentry对象
f、释放锁

get方法:
a、为输入的key做hash运算,得到hash值
b、通过hash值,定位到指定的segment对象
c、再次通过hah值,定位到segment当中数组的具体位置。

3、segment都各自加锁,那在调用size方法的时候,如何解决一致性问题?
size方法的目的是统计concurrenthashmap的总元素数量,自然需要把各个segment内部的元素数量汇总起来。但是,如果在统计segment元素数量的过程中,已统计过的segment瞬间插入新的元素,要怎么处理?
在这里插入图片描述
在这里插入图片描述
concurrentHashMap的size方法是一个嵌套循环,大体逻辑如下:
1.遍历所有的Segment。
2.把Segment的元素数量累加起来。
3.把Segment的修改次数累加起来。
4.判断所有Segment的总修改次数是否大于上一次的总修改次数。如果大于,说明统计过程中有修改,重新统计,尝试次数+1;如果不是。说明没有修改,统计结束。
5.如果尝试次数超过阈值,则对每一个Segment加锁,再重新统计。
6.再次判断所有Segment的总修改次数是否大于上一次的总修改次数。由于已经加锁,次数一定和上次相等。
7.释放锁,统计结束。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章