什麼是ConcurrentHashMap?不同JDK下ConcurrentHashMap的區別?

欢乐豆123發表於2024-03-07

什麼是ConcurrentHashMap?不同JDK下ConcurrentHashMap的區別?

一、HashMap執行緒安全

我們知道,在併發情況下,使用HashMap會有執行緒安全的問題,那麼如何避免呢?

想要避免Hashmap的執行緒安全問題有很多辦法,比如改用HashTable或者Collections.synchronizedMap

但是,這兩者有著共同的問題:效能。無論讀操作還是寫操作,他們都會給整個集合加鎖,導致同一時間的其他操作為之阻塞。

在併發環境下,如何能夠兼顧執行緒安全和執行效率呢?這時候ConcurrentHashmap就應運而生來。

二、ConcurrentHashMap

ConcurrentHashMap優勢就是採用了[鎖分段技術],每一個Segment就好比自治區,讀寫操作高度自治,Segment之間互不影響。

1. Segment

這裡面涉及到一個比較關鍵的概念:Segment。

Segment本身就相當於一個HashMap物件。同HashMap一樣,Segment包含一個HashEntry陣列,陣列中的每一個HashEntry既是一個鍵值對,也是一個連結串列的頭節點。

單一的Segment結構如下:

像這樣的Segment物件,在ConcurrentHashMap集合中有2的N次方個,共同儲存在一個名為segments的陣列當中。

因此整個ConcurrentHashMap的結構如下:

可以說,ConcurrentHashMap是一個二級雜湊表。在一個總的雜湊表下面,有若干個子雜湊表。

這樣的二級結構,和資料庫的水平拆分有些相似。

2. ConcurrentHashMap併發讀寫的幾種情形

1)Case1: 不同Segment的併發寫入

說明:不同Segment的寫入是可以併發執行的。

2)Case2: 同一Segment的一寫一讀

說明:同一Segment的寫和讀是可以併發執行的。

3)Case3:同一Segment的併發寫入

說明:Segment的寫入是需要上鎖的,因此對同一Segment的併發寫入會被阻塞。

由此可見,ConcurrentHashMap當中每個Segment各自持有一把鎖。在保證執行緒安全的同時降低了鎖的粒度,讓併發操作效率更高。

3. ConcurrentHashMap讀寫的詳細過程

1)Get方法

  • 為輸入的Key做Hash運算,得到hash值。
  • 透過hash值,定位到對應的Segment物件
  • 再次透過hash值,定位到Segment當中陣列的具體位置。

2)Put方法

  • 為輸入的Key做Hash運算,得到hash值。
  • 透過hash值,定位到對應的Segment物件
  • 獲取可重入鎖
  • 再次透過hash值,定位到Segment當中陣列的具體位置。
  • 插入或覆蓋HashEntry物件。

說明:從步驟可以看出,ConcurrentHashMap在讀寫時都需要二次定位。首先定位到Segment,之後定位到Segment內的具體陣列下標。

4. 呼叫size方法時,如何解決一致性問題?

這個問題Key理解為:既然每一個Segment都各自加鎖,那麼在呼叫Size方法的時候,怎麼解決一致性的問題呢?

分析:

Size方法的目的是統計ConcurrentHashMap的總元素數量, 自然需要把各個Segment內部的元素數量彙總起來。

但是,如果在統計Segment元素數量的過程中,已統計過的Segment瞬間插入新的元素,這時候該怎麼辦呢?

參考資料:《程式設計師小灰》微信公眾號

相關文章