ConcurrentHashMa源碼

歸納:java

        這個哈希表的主要設計目標是維護併發可讀性(一般方法get(),但也迭代器和相關方法),同時最小化更新爭用。node

        次要的目標是保持空間消耗與java.util相同或更好。數組

        HashMap,並支持high多個線程對空表的初始插入率。併發

 

        這個映射一般充當一個binned (bucked)哈希表。每個鍵值映射保存在節點中。大多數節點都是實例包含散列、鍵、值和next的基本節點類字段。線程

然而,存在各類子類:treenode排列成平衡的樹,而不是列表。樹冠支撐着樹根一組樹狀陽極。轉發節點位於頭部調整大小期間的桶。reservationnode用做設計

佔位符,同時在computeIfAbsent和相關的方法。類型爲TreeBin、forwarding節點和ReservationNode不保存正常的用戶密鑰、值或散列,以及在搜索過程當中容易區分等。由於它們有負哈希字段和空鍵和值字段。(這些特殊的節點不是不常見就是短暫的,所以,攜帶一些未使用的字段的影響是微不足道。)指針

 

        時,將該表惰性地初始化爲2的冪級大小第一次插入。表中的每一個bin一般包含一個節點列表(一般,列表只有0個或一個節點)。表訪問須要volatile/原子讀、寫和案件。由於沒有其餘方法來安排這個添加進一步的間接,咱們使用了intrinsics (sun.misc.Unsafe)操做。對象

 

        咱們使用節點哈希字段的頂部(符號)位進行控制目的——因爲尋址,不管如何它都是可用的約束。具備負哈希字段的節點是特殊的在map方法中處理或忽略。索引

 

        方法中第一個節點的插入(經過put或其變體)空桶是經過將其裝箱到桶中來執行的。這是到目前爲止,put操做在most下最多見的狀況鍵/散列分佈。其餘更新操做(插入、刪除和替換)須要鎖。咱們不想浪費將一個不一樣的鎖對象與之關聯所需的空間每一個bin,所以使用bin列表自己的第一個節點做爲一個鎖。對這些鎖的鎖定支持依賴於內置監控「同步」。ip

        將列表的第一個節點用做鎖自己並不會這樣作足夠了:當一個節點被鎖定時,任何更新都必須首先進行確認它仍然是鎖定後的第一個節點,而且若是沒有,請重試。由於新節點老是附加到列表中,一旦一個節點在一個bin中第一個被刪除,它就會保持在第一個直到被刪除或bin(調整大小後)失效。

 

每一個bin鎖的主要缺點是其餘更新在受相同保護的bin列表中的其餘節點上執行操做lock可能會中止,例如當user =()或映射時功能須要很長時間。然而,據統計,

隨機哈希碼,這不是一個常見的問題。理想狀況下,箱中節點的頻率服從泊松分佈(http://en.wikipedia.org/wiki/Poisson_distribution)給定調整閾值,參數平均約爲0.5爲0.75,但因爲調整大小,方差較大粒度。忽略方差,指望發生的列表大小k是(exp(-0.5) * pow(0.5, k) / factorial(k))。的

第一個值是:

0:    0.60653066
1:    0.30326533

2:    0.07581633
3:    0.01263606
4:    0.00157952
5:    0.00015795
6:    0.00001316
7:    0.00000094
8:    0.00000006

多於:少於千分之一

兩個訪問不一樣的線程的鎖爭用機率元素大約是隨機哈希下的1 /(8 * #元素)。

        實際中遇到的哈希代碼分佈有時明顯偏離統一的隨機性。這個包括n>時的狀況(1<<30),所以某些鍵必須碰撞。一樣地,對於多個鍵都是設計爲具備相同的哈希代碼或僅不一樣的哈希代碼隱藏的高位。因此咱們使用了一個二級策略當bin中的節點數超過閾值。這些樹鍵使用平衡樹來保存節點(a特殊形式的紅黑樹),邊界搜索時間O(log n)。treebin中的每一個搜索步驟至少是和普通的列表同樣慢,但考慮到n不能超過(1<<64)(在地址用完以前)此邊界搜索步進、鎖定保持時間等,達到合理的常數(大體每一個操做檢查100個節點(最壞狀況),只要鍵具備可比性(很是常見——字符串、長字符串等)。treebin節點(treenodes)也保持相同的「next」遍歷指針做爲常規節點,所以能夠遍歷以一樣的方式使用迭代器。

當佔用率超過某個百分比時,將調整表的大小。閾值(名義上爲0.75,但見下文)。任何線程注意到垃圾箱過滿可能有助於在啓動線程分配並設置替換數組。可是,這些其餘線程可能會繼續運行,而不是中止運行。插入等。使用treebins保護咱們不受調整大小時過分填充的最壞狀況影響進步。經過一個接一個地傳送垃圾箱來調整收益大小,從桌子到下一張桌子。然而,線程聲稱很小以前要傳輸的索引塊(經過字段transferIndex)這樣作能夠減小爭用。田間的世代印記sizectl確保大小調整不會重疊。由於咱們是使用兩次擴展的力量,每一個箱子的元件必需要麼保持同一索引,要麼以2的冪移動偏移量。咱們經過捕獲舊節點能夠重用的狀況,由於它們的下一個字段不會改變的。平均來講,只有大約六分之一的人須要當表翻倍時進行克隆。它們替換的節點將是一旦再也不被引用,即可收集的垃圾任何可能同時處於遍歷表。在傳輸時,舊的表bin包含只有一個特殊的轉發節點(哈希字段爲「moved」)。包含下一個表做爲其鍵。遇到一個轉發節點,訪問和更新操做從新啓動,使用新桌子。

相關文章
相關標籤/搜索