由於hashmap
是乙個執行緒不安全的容器,主要體現在容量大於總量*負載因子
發生擴容時會出現環形鍊錶從而導致死迴圈。
因此需要支援執行緒安全的併發容器concurrenthashmap
。
如圖所示,是由segment
陣列、hashentry
陣列組成,和hashmap
一樣,仍然是陣列加鍊表組成。
concurrenthashmap
採用了分段鎖技術,其中segment
繼承於reentrantlock
。不會像hashtable
那樣不管是put
還是get
操作都需要做同步處理,理論上 concurrenthashmap 支援currencylevel
(segment 陣列數量)的執行緒併發。每當乙個執行緒占用鎖訪問乙個segment
時,不會影響到其他的segment
。
concurrenthashmap
的get
方法是非常高效的,因為整個過程都不需要加鎖。
內部hashentry
類 :
static final class hashentry
}
首先也是通過 key 的 hash 定位到具體的 segment,在 put 之前會進行一次擴容校驗。這裡比 hashmap 要好的一點是:hashmap 是插入元素之後再看是否需要擴容,有可能擴容之後後續就沒有插入就浪費了本次擴容(擴容非常消耗效能)。
而 concurrenthashmap 不一樣,它是在將資料插入之前檢查是否需要擴容,之後再做插入操作。
每個segment
都有乙個volatile
修飾的全域性變數count
,求整個concurrenthashmap
的size
時很明顯就是將所有的count
累加即可。但是volatile
修飾的變數卻不能保證多執行緒的原子性,所有直接累加很容易出現併發問題。
但如果每次呼叫size
方法將其餘的修改操作加鎖效率也很低。所以做法是先嘗試兩次將count
累加,如果容器的count
發生了變化再加鎖來統計size
。
至於concurrenthashmap
是如何知道在統計時大小發生了變化呢,每個segment
都有乙個modcount
變數,每當進行一次put remove
等操作,modcount
將會 +1。只要modcount
發生了變化就認為容器的大小也在發生變化。
1.8 中的 concurrenthashmap 資料結構和實現與 1.7 還是有著明顯的差異。
其中拋棄了原有的 segment 分段鎖,而採用了cas + synchronized
來保證併發安全性。
也將 1.7 中存放資料的 hashentry 改為 node,但作用都是相同的。
其中的val next
都用了 volatile 修飾,保證了可見性。
重點來看看 put 函式:
1.8 在 1.7 的資料結構上做了大的改動,採用紅黑樹之後可以保證查詢效率(o(logn)
),甚至取消了 reentrantlock 改為了 synchronized,這樣可以看出在新版的 jdk 中對 synchronized 優化是很到位的。
礦洞程式設計師.jpg
ConcurrentHashMap實現原理
concurrenthashmap實現原理 在jdk1.7中 concurrenthashmap是通過segment陣列 hashentry陣列 單鏈表的結構進行儲存資料。segment陣列中存放的是hashentry陣列的首位址,hashentry中存放的是乙個單鏈表 首節點位址 put 我們通過...
ConcurrentHashMap儲存原理
concurrenthashmap是併發雜湊對映表的實現,它允許多執行緒環境完全併發讀取,並且支援16個執行緒併發更新。相對於hashtable和同步包包裝的hashmap collections.synchronizedmap new hashmap 具有更高的併發性。在hashtable和同步包...
ConcurrentHashMap底層原理
出自jdk5新引進的concurrent包,concurrenthashmap主要解決了兩個問題 相較於只使用synchronized的hashtable提高了效能,根據具體場景進行不同的設計,盡量避免了重量級鎖。不同於hashmap,採用了fail safe弱一致性迭代器,再迭代器使用過程中,可以...