ConcurrentHashMap 原始碼閱讀小結

2022-07-04 12:15:14 字數 2523 閱讀 4452

每一次總結都意味著重新開始,同時也是為了更好的開始。concurrenthashmap 一直是我心中的痛。雖然不敢說完全讀懂了,但也看了幾個重要的方法,有不少我覺得比較重要的知識點。

concurrenthashmap 擴容分析拾遺

併發程式設計——concurrenthashmap#addcount() 分析

併發程式設計——concurrenthashmap#transfer() 擴容逐行分析

併發程式設計——concurrenthashmap#helptransfer() 分析

併發程式設計 —— concurrenthashmap size 方法原理分析

併發程式設計之 concurrenthashmap(jdk 1.8) putval 原始碼分析

深入理解 hashmap put 方法(jdk 8逐行剖析)

深入理解 hashcode 和 hash 演算法

說起 concurrenthashmap ,當然從入口開始說。該方法要點如下:

不允許有 null key 和 null value。

只有在第一次 put 的時候才初始化 table。初始化有併發控制。通過 sizectl 變數判斷(小於 0)。

當 hash 對應的下標是 null 時,使用 cas 插入元素。

當 hash 對應的下標值是 forward 時,幫助擴容,但有可能幫不了,因為每個執行緒預設 16 個桶,如果只有 16個桶,第二個執行緒是無法幫助擴容的。

如果 hash 衝突了,同步頭節點,進行鍊錶操作,如果鍊錶長度達到 8 ,分成紅黑樹。

呼叫 addcount 方法,對 size 加一,並判斷是否需要擴容(如果是覆蓋,就不呼叫該方法)。

cmap 的併發效能是 hashtable 的 table.length 倍。只有出現鍊錶才會同步,否則使用 cas 插入。效能極高。

size 方法不準確,原因是由於併發插入,basecount 難以及時更新。計數盒子也難以及時更新。

內部通過兩個變數,乙個是 basecount,乙個是 countercells,countercells 是併發修改 basecount 後的備用方案。

具體更新 basecount 和 countercells 是在 addcount 方法中。備用方法 fulladdcount 則會死迴圈插入。

countercell 是乙個用於分配計數的填充單元,改編自 longadder和striped64。內部只有乙個 volatile 的 value 變數,同時這個類標記了@sun.misc.contended,這是乙個避免偽共享的註解,用於替代之前的快取行填充。多執行緒情況下,註解讓效能提公升 5 倍。

當 cmap 嘗試插入的時候,發現該節點是 forward 型別,則會幫助其擴容。

每次加入乙個執行緒都會將 sizectl 的低 16 位加一。同時會校驗高 16 位的標示符。

擴容最大的幫助執行緒是 65535,這是低 16 位的最大值限制的。

每個執行緒預設分配 16 個桶,如果桶的數量是 16,那麼第二個執行緒無法幫助其擴容。

該方法會根據 cpu 核心數平均分配給每個 cpu 相同數量的桶。但如果不夠 16 個,預設就是 16 個。

擴容是按照 2 倍進行擴容。

每個執行緒在處理完自己領取的區間後,還可以繼續領取,如果有的話。這個是 transferindex 變數遞減 16 實現的。

每次處理空桶的時候,會插入乙個 forward 節點,告訴 putval 的執行緒:「我正在擴容,快來幫忙」。但如果只有 16 個桶,只能有乙個執行緒擴容。

如果有了佔位符,那就不處理,跳過這個桶。

如果有真正的實際值,那就同步頭節點,防止 putval 那裡併發。

同步塊裡會將鍊錶拆成兩份,根據 hash & length 得到是否是 0,如果是0,放在低位,反之,反之放在 length + i 的高位。這裡的設計是為了防止下次取值的時候,hash 不到正確的位置。

如果該桶的型別是紅黑樹,也會拆成 2 個,這是必須的。然後判斷拆分過的桶的大小是否小於等於 6,如果是,改成鍊錶。

執行緒處理完之後,如果沒有可選區間,且任務沒有完成,就會將整個表檢查一遍,防止遺漏。

當插入結束的時候,會對 size 進行加一。也會進行是否需要擴容的判斷。

優先使用計數盒子(如果不是空,說明併發了),如果計數盒子是空,使用 basecount 變數。對其加 x。

如果修改 basecount 失敗,使用計數盒子。如果此次修改失敗,在另乙個方法死迴圈插入。

檢查是否需要擴容。

如果 size 大於等於 sizectl 閾值,且長度小於 1 << 30,可以擴容成 1 << 30,但不能擴容成 1 << 31。

如果已經在擴容,幫助其擴容,和 helptransfer 邏輯一樣。

如果沒有在擴容,自行開啟擴容,更新 sizectl 變數為負數,賦值為識別符號高 16 位 + 2。

concurrenthashmap 滿是財富,都是精華**,我們這次閱讀只是管中窺豹,要知道其中包含 53 個類,6300 行**,但這次確實收穫很多。有時間一定再次閱讀!!

能力不高,水平有限,有些地方確實理解不了 doug lea 大師的設計,如果有什麼錯誤,還請大家指出。不勝感激。

ConcurrentHashMap原始碼分析

hashmap 先說hashmap,hashmap是執行緒不安全 的,在併發環境下,可能會形成環狀鍊錶 hashtable hashtable和hashmap的實現原理幾乎一樣,差別無非是1.hashtable不允許key和value為null 2.hashtable是執行緒安全的。但是hashta...

ConcurrentHashMap原始碼詳解

成員變數private static final int maximum capacity 1 30 private static final int default capacity 16 static final int max array size integer.max value 8 pr...

concurrentHashMap原始碼分析

concurrenthashmap是hashmap的執行緒安全版本,內部也是使用 陣列 鍊錶 紅黑樹 的結構來儲存元素。相比於同樣執行緒安全的hashtable來說,效率等各方面都有極大地提高。在這裡可以使用上篇那個 進行測試,根據結果可以知道concurrenthashmap是執行緒安全的,由於分...