ConcurrentHashMap原始碼解析

2021-10-07 19:54:28 字數 3995 閱讀 5820

/**

* implementation for put and putifabsent

* onlyifabsent為true時,key值已存在則不修改

*/final v putval(k key, v value, boolean onlyifabsent)

// [3]先賦值操作,將f的hash賦值給fh;再判斷,如果已經初始化且元素所在陣列位置上不為空,判斷是否在擴容

// 【hash = moved說明,當前正在進行擴容且該位置上節點已經被處理過了】,如果在擴容下面就幫助擴容

// ps:(個人見解:hash != moved,當前也可能正在進行擴容,只不過陣列當前位置上的元素還沒被處理過,

// 所以現在在該位置插入資料不影響後面擴容的時候處理該位置的資料)

else if ((fh = f.hash) == moved)

// 幫助進行擴容操作

tab = helptransfer(tab, f);

// [4]如果已經初始化且對應陣列下標位置上不為空,且沒有正在進行擴容(或已經在擴容了,只不過當前下標位置還沒有進行處理),則進行插入元素

else

nodepred = e;

// 變數e指向鍊錶下乙個元素,看是否為空;

// 不為空則繼續迴圈並將bincount+1,在上面判斷此時的e.key是否等於put進來的key;

// 為空說明該key以前不存在,put進來的key為新值,插到末尾

if ((e = e.next) == null) }}

// 如果f位置上是紅黑樹結構

else if (f instanceof treebin) }}

}// bincount == 0,說明沒有進行put操作,可能拿到鎖後進行檢驗f已經被其他執行緒修改了

if (bincount != 0) }}

// bincount數值:

// 1)、f為空時:bincount == 0

// 2)、f為鍊錶時:bincount == 鍊錶中與key值相等的節點的長度位置(可能不等於鍊錶長度,已存在情況下可能中間位置就找到了),新增元素時肯定等於鍊錶長度-1(新增鍊錶節點的時候直接break了,沒有執行addcount++)

// 3)、f為紅黑樹時:bincount == 2

addcount(1l, bincount);

return null;

}

/**

* moves and/or copies the nodes in each bin to new table. see

* above for explanation.

* 擴容方法

*/private final void transfer(node tab, node nexttab) catch (throwable ex)

nexttable = nexttab;

// 原陣列長度n賦值給擴容索引transferindex

transferindex = n;

}// 將新陣列長度賦值給nextn

int nextn = nexttab.length;

// 建立forwardingnode物件,並將新陣列存到forwardingnode中去

forwardingnodefwd = new forwardingnode(nexttab);

// 推進狀態

boolean advance = true;

// 擴容是否結束

boolean finishing = false; // to ensure sweep before committing nexttab

// 迴圈

// i是陣列位置索引

// 步幅下邊界索引

for (int i = 0, bound = 0;;)

// 說明陣列table還有步幅沒有執行緒去處理,需要領取該步幅去幫助擴容

// nextbound:如果剩下未同處理的部分長度(transferindex數值上等於未處理部分長度)不大於步幅[stride],那就預設nextbound = 0(正常最後乙個步幅剛好=transferindex)

// 用cas去修改transferindex為nextbound,如果失敗說明有其他執行緒已經領取了這個步幅去處理了

// cas成功說明成功領取到該步幅([transferindex - stride,transferindex - 1])

else if (u.compareandswapint

(this, transferindex, nextindex,

nextbound = (nextindex > stride ?

nextindex - stride : 0)))

}// 當前執行緒已經完成了幫助擴容操作

// i < 0 兩種可能:

// 1)、判斷條件 --i導致;i減到-1說明陣列所有步幅已經全部都有執行緒去處理了

// 2)、判斷條件((nextindex = transferindex) <= 0)為true時,執行**i = -1;transferindex <= 0說明所有步幅已經全部都有執行緒去處理了

// 說明已經沒有步幅沒有執行緒去處理了,自己的步幅也完成了

if (i < 0 || i >= n || i + n >= nextn)

// 幫助擴容完成,將sizectl - 1,(因為幫助擴容的執行緒會在幫助擴容之前將sizectl + 1)

if (u.compareandswapint(this, sizectl, sc = sizectl, sc - 1))

}// 將tab[i]賦值給f,判斷f是否為空,為空則插入forwardingnode節點佔位

else if ((f = tabat(tab, i)) == null)

advance = castabat(tab, i, null, fwd);

// 判斷f.hash是否等於moved -1(forwardingnode.hash = moved),是說明已經被處理過了(recheck的時候全部走這乙個判斷)

else if ((fh = f.hash) == moved)

advance = true; // already processed

// 沒有被處理過的位置(擴容需要處理的位置)

else

}// lastrun賦值給低位鍊錶

if (runbit == 0)

// lastrun賦值給高位鍊錶

else

// 遍歷鍊錶,將節點全部挪到新陣列

for (nodep = f; p != lastrun; p = p.next)

// 將低位鍊錶放到新陣列對應位置

settabat(nexttab, i, ln);

// 將高位鍊錶放到新陣列對應位置

settabat(nexttab, i + n, hn);

// 將原陣列處理過的位置(tab[i])插入forwardingnode標識已經被處理過

settabat(tab, i, fwd);

advance = true;

}else if (f instanceof treebin)

else

}ln = (lc <= untreeify_threshold) ? untreeify(lo) :

(hc != 0) ? new treebin(lo) : t;

hn = (hc <= untreeify_threshold) ? untreeify(hi) :

(lc != 0) ? new treebin(hi) : t;

settabat(nexttab, i, ln);

settabat(nexttab, i + n, hn);

settabat(tab, i, fwd);

advance = true;}}

}}

}}

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是執行緒安全的,由於分...