hashmap在設計之初並沒有考慮多執行緒併發的情況,多執行緒併發的情況下理論上應該使用concurrenthashmap,但是程式中經常會無意中在多併發的情況下使用了hashmap,如果是jdk1.8以下的版本,有可能會導致死迴圈,打滿cpu占用,下面基於jdk1.7原始碼分析下原因。
我們從put看起
public v put(k key, v value)
if (key == null)
return putfornullkey(value);
int hash = hash(key);
int i = indexfor(hash, table.length);
for (entrye = table[i]; e != null; e = e.next)
}modcount++;
addentry(hash, key, value, i);
return
null;
}
addentry方法長這樣:
void addentry(int hash, k key, v value, int bucketindex)
createentry(hash, key, value, bucketindex);
}
下面就是resize方法:
void resize(int newcapacity)
entry newtable = new entry[newcapacity];
transfer(newtable, inithashseedasneeded(newcapacity));
table = newtable;
threshold = (int)math.min(newcapacity * loadfactor, maximum_capacity + 1);
}
然後就是關鍵方法transfer:
void transfer(entry newtable, boolean rehash)
int i = indexfor(e.hash, newcapacity);
e.next = newtable[i];
newtable[i] = e;
e = next;}}
}
thread1:
e=anext=b
舊鍊錶:
a->b->
null
新鍊錶:
null
thread1:
e=anext=b
舊鍊錶:
null
新鍊錶:
b->a->
null
thread1:
e=anext=b
舊鍊錶:
null
新鍊錶:
b->a->b(出現迴圈)
thread1:
e=anext=b
舊鍊錶:
null
新鍊錶:
a->b->a
thread1:
e=bnext=b
舊鍊錶:
null
新鍊錶:
a->b->a
那麼對於jdk1.8是如何做的呢,1.8的resize比較複雜,核心點在下面:
nodelohead = null, lotail = null;
nodehihead = null, hitail = null;
nodenext;
do else
} while ((e = next) != null);
HashMap擴容死迴圈問題解析
下面記錄了一下學習過程和自己的理解。當插入乙個新的鍵值對時,會先根據 key 對 hashmap底層陣列長度取模,得到鍵值對應該存放的陣列下標,然後呼叫 addentry 函式把這個鍵值對插入到這個下標所在的鍊錶中 void addentry int hash,k key,v value,int b...
多執行緒 HashMap 死迴圈 問題解析
原始碼resize void resize int newcapacity 複製 遷移 void transfer entry newtable while e null 複製 我們可以知道newtable 是新建立的 是執行緒私有的,因為 執行緒1 獲取 e 和next 之後 執行緒2 插入執行了...
HashMap 多執行緒 死迴圈 Java
hashmap,眾所周知,是執行緒不安全的。在多執行緒的情況下,在get 非常有可能出現死迴圈。因為 hashmap採用鍊錶解決hash衝突,因為是鍊錶結構,那麼就很容易形成閉合的鏈路,這樣在迴圈的時候只要有執行緒對這個hashmap進行get操作就會產生死迴圈。只 有乙個執行緒對hashmap的資...