ConCurrentHashMap的put操作主要由putVal()方法實作,該方法中對value的插入,采用了CAS操作和synchronized的操作,從而保證了并發環境下的安全性,
put步驟大致如下:
- 通過key計算得到hashcode
- 判斷是否需要進行初始化(采用了延遲初始化的策略Lazy table initialization minimizes footprint until first use)
- 利用hash值定位Node,如果當前位置沒有Node,則依據CAS機制嘗試插入,如果插入失敗,則通過自旋保證插入成功
- 判斷是否需要進行擴容,如果需要進行擴容,則執行helpTransfer方法
- 如果是在無需進行初始化,hash值計算得到的位置存在Node,并且無需擴容的情況下,則利用synchronized鎖來寫入資料
- 上述操作后,如果當前數量超過了TREEIFY_THRESHOLD(8,跟HashMap中的值大小相同),則轉化為紅黑樹結構,(注意,上述標藍的4步,只會執行其中一步)
ConCurrentHashMap的put操作的原始碼如下(含注釋):
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
// 根據key計算出hash code
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
// 判斷是否需要進行初始化
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
// 根據計算得出的hash定位Node的位置,如果為空,則依據CAS機制嘗試插入,如果失敗,則通過自旋保證成功
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
// 判斷是否需要擴容
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
// 利用synchronized鎖進行寫入資料
V oldVal = null;
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;
break;
}
Node<K,V> pred = e;
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
else if (f instanceof TreeBin) {
Node<K,V> p;
binCount = 2;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
// 如果數量大于TREEIFY_THRESHOLD,則轉化為紅黑樹
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/274860.html
標籤:其他
上一篇:執行緒安全的list
下一篇:2021-04-10
