java基礎之HashMap導致死迴圈以及ConcurrentHashMap和HashTable
HashMap多執行緒操作可能導致死迴圈問題
主要原因在於併發下的Rehash會造成元素之間形成一個迴圈連結串列。不過jdk1.8後解決了這個問題,但還是不建議在多執行緒下使用HashMap,因為多執行緒下使用HashMap還是會存在其他問題比如資料丟失。併發環境下推薦使用ConcurrentHashMap。
ConcurrentHashMap和Hashtable的區別
主要體現在實現執行緒安全的方式不同:
-
底層資料結構:JDK1.7的ConcurrentHashMap底層採用分段陣列+連結串列 實現,JDK1.8採用的資料結構跟HashMap1.8的結構一樣,陣列+連結串列/紅黑二叉樹 。Hashtable和JDK1.8之前的HashMap的底層資料結構都是採用陣列+連結串列的形式,陣列是HashMap的主體,連結串列則是主要為了解決雜湊衝突而存在的。
-
實現執行緒安全的方式:① 在JDK1.7 的時候ConcurrentHashMap(分段鎖)對整個桶陣列進行了分割分段(Segment),每一把鎖只鎖容器其中一部分資料,多執行緒訪問容器裡不同資料段的資料,就不會存在鎖競爭,提高了併發訪問率 ,到了JDK1.8 的時候就已經摒棄了segment的概念,而是直接用Node陣列+連結串列+紅黑樹的結構來實現,併發控制使用synchronized和CAS來操作(JDK1.6以後對synchronized鎖做了很多優化 )整個看起來像是優化過且執行緒安全的HashMap,雖然在JDK1.8中還能看到Segment的資料結構,但已經簡化了屬性,只是為了相容舊版本;②
ConcurrentHashMap執行緒安全的具體實現方式/底層具體實現
JDK1.7如上圖
首先將資料分成一段一段的儲存,然後給每段資料分配一把鎖,當一個執行緒佔用鎖訪問其中一段資料時,其他段的資料也能被其他執行緒訪問。
ConcurrentHashMap是由Segment陣列結構和HashEnty陣列結構組成
Segment實現了ReetrantLock,所以Sement是一種可重入鎖,扮演鎖的角色。HashEntry用於儲存鍵值對資料。
一個ConurrentHashMap裡包含一個Segment陣列,Segment的結構和HashMap類似,是一種陣列和連結串列結構,一個Segment包含HashEntry陣列,每個HashEntry是一個連結串列結構的元素,每個Segment守護著一個HashEntry數組裡的元素,當對HashEntry陣列的資料進行修改時,必須先獲得對應的Segment的鎖。
JDK1.8如上圖
ConcurrentHashMap取消了Segment分段鎖,採用CAS和synchronized來保證併發安全。資料結構跟HashMap1.8的結構類似,陣列+連結串列/紅黑二叉樹。Java8在連結串列長度超過一定閾值(8 )時將連結串列轉換成紅黑樹