每一次總結都意味著重新開始,同時也是為了更好的開始。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是執行緒安全的,由於分...