什麼是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.釋放鎖,統計結束。

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