ConcurrentHashMap get和put方法解析(借鑒大神的和理解的)


原文出處 https://blog.csdn.net/liaob0106/article/details/91878302

首先把那些惡心的變量解釋一下

變量名稱 含義
bincount table里目標索引鏈表的元素個數
f table里目標索引對應鏈表的頭結點
n table的長度
i 目標索引
fh 頭結點f的哈希值
tab table數組的副本

final V putVal(K key, V value, boolean onlyIfAbsent) {
//1、判斷key是否為空
if (key == null || value == null) throw new NullPointerException();
//2、計算哈希值
int hash = spread(key.hashCode());
int binCount = 0;
//3、得到table的數組
for (Node<K, V>[] tab = table; ; ) {

        Node<K, V> f;
        int n, i, fh;

//4.如果table數組為空,則初始化table
if (tab == null || (n = tab.length) == 0)
tab = initTable();
//5.如果對應key的哈希值上對應table數組下標的位置沒有node,則通過cas操作創建一個node放入table中,然后putval出棧
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node<K, V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
//6、如果table正在擴容,則得到擴容后的table,然后再重新開始一個循環
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
//7.到這里說明找到了key hash后對應的table,並且table上有其他node的存在
V oldVal = null;
//8、把這個找到的node加上同步鎖,防止並發出現的問題,如果其他key put進來的時候也對應這個tab則堵塞在這里
synchronized (f) {
//9.再次用cas確認索引i上的table為我們找到的node,如果不是的話則這個node被修改,直接釋放鎖進入下一個循環
if (tabAt(tab, i) == f) {
//10.如果目標table的第一個node的哈希值大於等於0,則是鏈式結構,走鏈表查找,反之走紅黑樹查找
if (fh >= 0) {
//11.標志bincount為1,因為在該table上至少有一個node節點
binCount = 1;
//12.循環鏈表
for (Node<K, V> e = f; ; ++binCount) {
K ek;
//13.如果遍歷元素的哈希值與需要插入目標key的哈希值相同,並且值也相同,則插入的是重復key的元素
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
//14.如果onlyIfAbsent為false的話,則替換為新value,否則不修改(一般傳false)
if (!onlyIfAbsent)
e.val = value;
//15.break循環
break;
}
//16.循環直到最后一個node節點的key都不是我們想要插入的key
Node<K, V> pred = e;
if ((e = e.next) == null) {
//在尾部添加一個新節點,break循環
pred.next = new Node<K, V>(hash, key,
value, null);
break;
}
}
}
//17.該節點屬於紅黑樹的子節點,進行樹操作
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;
}
}
}
}
//18.如果node節點不為0
if (binCount != 0) {
//19.如果node大於或者等於8,則轉為紅黑樹
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
//20.返回原來key對應的舊值
if (oldVal != null)
return oldVal;
break;
}
}
}
//20.進行擴容判斷
addCount(1L, binCount);
return null;
}

get方法:
public V get(Object key) {
    Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;

//根據這個key算hashcode 與1.7相比沒有異或四次 效率更高了
int h = spread(key.hashCode());
//整個 hashtable對象 table中 tabAt 根據這個key算出來的hash值找對應的value 不為空,看找到的e 返回e的值 。
//由於tab這個節點的val變量是被 volatile修飾 重新賦值(單純賦值不含任何計算)會被其他線程所知曉。所以get方法不用加鎖。
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) {
if ((eh = e.hash) == h) {
if ((ek = e.key) == key || (ek != null && key.equals(ek)))
return e.val;
}
else if (eh < 0)
return (p = e.find(h, key)) != null ? p.val : null;
while ((e = e.next) != null) {
if (e.hash == h &&
((ek = e.key) == key || (ek != null && key.equals(ek))))
return e.val;
}
}
return null;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM