ConcurrentHashMap底層結構(JDK1.7)

HashMap不是線程安全的,HashTable雖然是線程安全,但是該類所有的方法都用synchronized進行線程安全的控制,在高併發的情況下,同一時刻只有一個線程可以獲取對象監視器,其他線程阻塞或者輪詢等待,在線程競爭激烈的情況下,這種方式的效率會非常的低下。

 

HashTable在擴容的時候,newSize = 2 * oldSize + 1;

 

ConcurrentHashMap是線程安全的,使用了鎖分段的思想提高了併發度。

ConcurrentHashMap爲什麼高效:

Hashtable低效主要是因爲所有訪問Hashtable的線程都爭奪一把鎖。如果容器有很多把鎖,每一把鎖控制容器中的一部分數據,那麼當多個線程訪問容器裏的不同部分的數據時,線程之前就不會存在鎖的競爭,這樣就可以有效的提高併發的訪問效率。這也正是ConcurrentHashMap使用的分段鎖技術。將ConcurrentHashMap容器的數據分段存儲,每一段數據分配一個Segment(鎖),當線程佔用其中一個Segment時,其他線程可正常訪問其他段數據。

 

jdk1.7下ConcurrentHashMap的數據結構:

image

ConcurrentHashMap包含一個Segment數組,每個Segment包含一個HashEntry數組,當修改HashEntry數組採用開鏈法處理衝突,所以它的每個HashEntry元素又是鏈表結構的元素。

查找元素時,先通過key定位到Segment的下標位置,再找到對應的HashEntry的下標位置,然後再比較key的值。

Segment是ConcurrentHashMap的一個內部類,主要組成如下:

HashEnrty源碼:


 

 

和HashMap非常類似,唯一的區別是核心的數據如value,以及鏈表都是volatile修飾的,保證了獲取時的可見性。

原理上來說,ConcurrentHashMap採用了分段鎖的技術,不會像HashTable那樣不管put和get都需要同步處理,

理論上ConcurrentHashMap支持Segment數組數量大小的線程併發,當一個線程佔用鎖訪問Segment時不會影

響到其他的Segment。

put()方法:

首先通過key定位到Segment

 

在具體的Segment再進行put,首先第一步的時候會嘗試獲取鎖,如果獲取失敗肯定就有其他線程存在競爭,則利用

scanAndLockForPut()自旋獲取鎖:

1.嘗試自旋獲取鎖

2.如果重試次數達到了MAX_SCAN_RETRIES則改爲阻塞鎖獲取成功,保證能獲取成功

get()方法:

 

get的邏輯比較簡單,key通過hash之後定位到具體的Segment,

再通過一次 Hash 定位到具體的元素上。

由於 HashEntry 中的 value 屬性是用 volatile 關鍵詞修飾的,保證了內存可見性,所以每次獲取時都是最新值。

ConcurrentHashMap 的 get 方法是非常高效的,因爲整個過程都不需要加鎖

 

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