1. 程式人生 > >併發程式設計系列:ConcurrentHashMap的實現原理(JDK1.7和JDK1.8)

併發程式設計系列:ConcurrentHashMap的實現原理(JDK1.7和JDK1.8)

轉載自:https://www.toutiao.com/i6623301848268800519/


HashMap、CurrentHashMap 的實現原理基本都是BAT面試必考內容,阿里P8架構師談:深入探討HashMap的底層結構、原理、擴容機制深入談過hashmap的實現原理,今天主要談CurrentHashMap的實現原理。

內容目錄:

  • 1.雜湊表
  • 2.ConcurrentHashMap與HashMap、HashTable的區別
  • 3.CurrentHashMap在JDK1.7和JDK1.8版本的區別

一、雜湊表

1.介紹

雜湊表(Hash table),根據關鍵碼值(Key value)而直接進行訪問的資料結構。也就是說,它通過把關鍵碼值對映到表中一個位置來訪問記錄,以加快查詢的速度。這個對映函式叫做雜湊函式,存放記錄的陣列叫做散列表。

雜湊表設計了一個對映關係f(key)= address,根據key來計算儲存地址address,這樣可以1次查詢,f既是儲存資料過程中用來指引資料儲存到什麼位置的函式,也是將來查詢這個位置的演算法,叫做雜湊演算法。

2.應用場景

我們熟知的快取技術(比如redis、memcached)的核心其實就是在記憶體中維護一張巨大的雜湊表,還有大家熟知的HashMap、CurrentHashMap等的應用。

二、ConcurrentHashMap與HashMap等的區別

1.HashMap

我們知道HashMap是執行緒不安全的,在多執行緒環境下,使用Hashmap進行put操作會引起死迴圈,導致CPU利用率接近100%,所以在併發情況下不能使用HashMap。

2.HashTable

HashTable和HashMap的實現原理幾乎一樣,差別無非是

HashTable不允許key和value為null
HashTable是執行緒安全的
但是HashTable執行緒安全的策略實現代價卻太大了,簡單粗暴,get/put所有相關操作都是synchronized的,這相當於給整個雜湊表加了一把大鎖,多執行緒訪問時候,只要有一個執行緒訪問或操作該物件,那其他執行緒只能阻塞,相當於將所有的操作序列化,在競爭激烈的併發場景中效能就會非常差。

3.ConcurrentHashMap

主要就是為了應對hashmap在併發環境下不安全而誕生的,ConcurrentHashMap的設計與實現非常精巧,大量的利用了volatile,final,CAS等lock-free技術來減少鎖競爭對於效能的影響。

我們都知道Map一般都是陣列+連結串列結構(JDK1.8該為陣列+紅黑樹),ConcurrentHashMap避免了對全域性加鎖改成了區域性加鎖操作,這樣就極大地提高了併發環境下的操作速度。

由於ConcurrentHashMap在JDK1.7和1.8中的實現非常不同,接下來我們談談JDK在1.7和1.8中的區別。

JDK1.7版本的CurrentHashMap的實現原理
在JDK1.7中ConcurrentHashMap採用了陣列+Segment+分段鎖的方式實現。

ConcurrentHashMap中的分段鎖稱為Segment,它即類似於HashMap的結構,即內部擁有一個Entry陣列,陣列中的每個元素又是一個連結串列,同時又是一個ReentrantLock(Segment繼承了ReentrantLock)。

ConcurrentHashMap使用分段鎖技術,將資料分成一段一段的儲存,然後給每一段資料配一把鎖,當一個執行緒佔用鎖訪問其中一個段資料的時候,其他段的資料也能被其他執行緒訪問,能夠實現真正的併發訪問。如下圖是ConcurrentHashMap的內部結構圖:
在這裡插入圖片描述

從上面的結構我們可以瞭解到,ConcurrentHashMap定位一個元素的過程需要進行兩次Hash操作。

第一次Hash定位到Segment,第二次Hash定位到元素所在的連結串列的頭部。

因此,這一種結構的帶來的副作用是Hash的過程要比普通的HashMap要長,但是帶來的好處是寫操作的時候可以只對元素所在的Segment進行加鎖即可,不會影響到其他的Segment,這樣,在最理想的情況下,ConcurrentHashMap可以最高同時支援Segment數量大小的寫操作(剛好這些寫操作都非常平均地分佈在所有的Segment上)。

所以,通過這一種結構,ConcurrentHashMap的併發能力可以大大的提高。

三、JDK1.8版本的CurrentHashMap的實現原理

JDK8中ConcurrentHashMap參考了JDK8 HashMap的實現,採用了陣列+連結串列+紅黑樹的實現方式來設計,內部大量採用CAS操作,JDK8中徹底放棄了Segment轉而採用的是Node,其設計思想也不再是JDK1.7中的分段鎖思想。

在JDK8中ConcurrentHashMap的結構,由於引入了紅黑樹,使得ConcurrentHashMap的實現非常複雜,我們都知道,紅黑樹是一種效能非常好的二叉查詢樹,其查詢效能為O(logN),但是其實現過程也非常複雜,而且可讀性也非常差,Doug Lea的思維能力確實不是一般人能比的,早期完全採用連結串列結構時Map的查詢時間複雜度為O(N),JDK8中ConcurrentHashMap在連結串列的長度大於某個閾值的時候會將連結串列轉換成紅黑樹進一步提高其查詢效能。

在這裡插入圖片描述

總結

1, 從JDK1.7到1.8,ConcurrentHashMap 的結構從ReentrantLock+Segment+HashEntry變化為synchronized+CAS+HashEntry+紅黑樹。

2.在擴容的情況下,雜湊衝突得到一定的處理,提高了效率。

3.在JDK1.8中ConcurrentHashMap和HashMap一樣,採取了紅黑樹結構,和連結串列選擇使用,提高了遍歷的速度。

以上就是ConcurrentHashMap的實現原理介紹,以下主要是從高併發架構設計的角度,利用分散式快取、非同步訊息等方式解決高併發場景,具體的內容如下: