ConcurrentHashMap 實現原理

2021-09-14 01:13:16 字數 2412 閱讀 6457

由於hashmap是乙個執行緒不安全的容器,主要體現在容量大於總量*負載因子發生擴容時會出現環形鍊錶從而導致死迴圈。

因此需要支援執行緒安全的併發容器concurrenthashmap

如圖所示,是由segment陣列、hashentry陣列組成,和hashmap一樣,仍然是陣列加鍊表組成。

concurrenthashmap採用了分段鎖技術,其中segment繼承於reentrantlock。不會像hashtable那樣不管是put還是get操作都需要做同步處理,理論上 concurrenthashmap 支援currencylevel(segment 陣列數量)的執行緒併發。每當乙個執行緒占用鎖訪問乙個segment時,不會影響到其他的segment

concurrenthashmapget方法是非常高效的,因為整個過程都不需要加鎖。

內部hashentry類 :

static final class hashentry

}

首先也是通過 key 的 hash 定位到具體的 segment,在 put 之前會進行一次擴容校驗。這裡比 hashmap 要好的一點是:hashmap 是插入元素之後再看是否需要擴容,有可能擴容之後後續就沒有插入就浪費了本次擴容(擴容非常消耗效能)。

而 concurrenthashmap 不一樣,它是在將資料插入之前檢查是否需要擴容,之後再做插入操作。

每個segment都有乙個volatile修飾的全域性變數count,求整個concurrenthashmapsize時很明顯就是將所有的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弱一致性迭代器,再迭代器使用過程中,可以...