1. 程式人生 > 其它 >ConcurrentHashMap 在 Java7 和 8 有何不同?

ConcurrentHashMap 在 Java7 和 8 有何不同?

Java 7 版本的 ConcurrentHashMap

結構示意圖:

從圖中我們可以看出,在 ConcurrentHashMap 內部進行了 Segment 分段,Segment 繼承了 ReentrantLock,可以理解為一把鎖,各個 Segment 之間都是相互獨立上鎖的,互不影響。相比於之前的 Hashtable 每次操作都需要把整個物件鎖住而言,大大提高了併發效率。因為它的鎖與鎖之間是獨立的,而不是整個物件只有一把鎖。

每個 Segment 的底層資料結構與 HashMap 類似,仍然是陣列和連結串列組成的拉鍊法結構。預設有 0~15 共 16 個 Segment,所以最多可以同時支援 16 個執行緒併發操作(操作分別分佈在不同的 Segment 上)。16 這個預設值可以在初始化的時候設定為其他值,但是一旦確認初始化以後,是不可以擴容的。

Java 8 版本的 ConcurrentHashMap

在 Java 8 中,幾乎完全重寫了 ConcurrentHashMap,程式碼量從原來 Java 7 中的 1000 多行,變成了現在的 6000 多行,所以也大大提高了原始碼的閱讀難度。

結構示意圖:

圖中的節點有三種類型。

第一種是最簡單的,空著的位置代表當前還沒有元素來填充。

第二種就是和 HashMap 非常類似的拉鍊法結構,在每一個槽中會首先填入第一個節點,但是後續如果計算出相同的 Hash 值,就用連結串列的形式往後進行延伸。

第三種結構就是紅黑樹結構,這是 Java 7 的 ConcurrentHashMap 中所沒有的結構,在此之前我們可能也很少接觸這樣的資料結構。

當第二種情況的連結串列長度大於某一個閾值(預設為 8),且同時滿足一定的容量要求的時候,ConcurrentHashMap 便會把這個連結串列從連結串列的形式轉化為紅黑樹的形式,目的是進一步提高它的查詢效能。所以,Java 8 的一個重要變化就是引入了紅黑樹的設計。

紅黑樹介紹

紅黑樹是每個節點都帶有顏色屬性的二叉查詢樹,顏色為紅色或黑色,紅黑樹的本質是對二叉查詢樹 BST 的一種平衡策略,我們可以理解為是一種平衡二叉查詢樹,查詢效率高,會自動平衡,防止極端不平衡從而影響查詢效率的情況發生。

由於自平衡的特點,即左右子樹高度幾乎一致,所以其查詢效能近似於二分查詢,時間複雜度是 O(log(n))

級別;反觀連結串列,它的時間複雜度就不一樣了,如果發生了最壞的情況,可能需要遍歷整個連結串列才能找到目標元素,時間複雜度為 O(n),遠遠大於紅黑樹的 O(log(n)),尤其是在節點越來越多的情況下,O(log(n)) 體現出的優勢會更加明顯。

紅黑樹的一些其他特點:

每個節點要麼是紅色,要麼是黑色,但根節點永遠是黑色的。

紅色節點不能連續,也就是說,紅色節點的子和父都不能是紅色的。

從任一節點到其每個葉子節點的路徑都包含相同數量的黑色節點。

正是由於這些規則和要求的限制,紅黑樹保證了較高的查詢效率,所以現在就可以理解為什麼 Java 8 的 ConcurrentHashMap 要引入紅黑樹了。好處就是避免在極端的情況下衝突連結串列變得很長,在查詢的時候,效率會非常慢。而紅黑樹具有自平衡的特點,所以,即便是極端情況下,也可以保證查詢效率在 O(log(n))

對比Java7 和Java8 的異同和優缺點

(1)資料結構

Java 7 採用 Segment 分段鎖來實現,而 Java 8 中的 ConcurrentHashMap 使用陣列 + 連結串列 + 紅黑樹,在這一點上它們的差別非常大。

(2)併發度

Java 7 中,每個 Segment 獨立加鎖,最大併發個數就是 Segment 的個數,預設是 16。

但是到了 Java 8 中,鎖粒度更細,理想情況下 table 陣列元素的個數(也就是陣列長度)就是其支援併發的最大個數,併發度比之前有提高。

(3)保證併發安全的原理

Java 7 採用 Segment 分段鎖來保證安全,而 Segment 是繼承自 ReentrantLock。

Java8 中放棄了 Segment 的設計,採用 Node + CAS + synchronized 保證執行緒安全。

(4)查詢時間複雜度

Java 7 遍歷連結串列的時間複雜度是 O(n),n 為連結串列長度。

Java8 如果變成遍歷紅黑樹,那麼時間複雜度降低為 O(log(n)),n 為樹的節點個數。

(5)遇到 Hash 碰撞

Java 7 在 Hash 衝突時,會使用拉鍊法,也就是連結串列的形式。

Java8 先使用拉鍊法,在連結串列長度超過一定閾值時,將連結串列轉換為紅黑樹,來提高查詢效率。

希望本文章對您有幫助,您的轉發、點贊是我的創作動力,十分感謝。更多好文推薦,請關注我的微信公眾號--JustJavaIt