【20期】你知道為什么HashMap是線程不安全的嗎?
閱讀本文大概需要 5.5?分鐘。
1.jdk1.7中的HashMap
public?class?HashMapTest?{
????public?static?void?main(String[]?args)?{
????????HashMapThread?thread0?=?new?HashMapThread();
????????HashMapThread?thread1?=?new?HashMapThread();
????????HashMapThread?thread2?=?new?HashMapThread();
????????HashMapThread?thread3?=?new?HashMapThread();
????????HashMapThread?thread4?=?new?HashMapThread();
????????thread0.start();
????????thread1.start();
????????thread2.start();
????????thread3.start();
????????thread4.start();
????}
}
class?HashMapThread?extends?Thread?{
????private?static?AtomicInteger?ai?=?new?AtomicInteger();
????private?static?Map?map?=?new?HashMap<>();
????@Override
????public?void?run()?{
????????while?(ai.get()?1000000)?{
????????????map.put(ai.get(),?ai.get());
????????????ai.incrementAndGet();
????????}
????}
}



void?transfer(Entry[]?newTable,?boolean?rehash)?{
????????int?newCapacity?=?newTable.length;
????????for?(Entry?e?:?table)?{
????????????while(null?!=?e)?{
????????????????Entry?next?=?e.next;
????????????????if?(rehash)?{
????????????????????e.hash?=?null?==?e.key???0?:?hash(e.key);
????????????????}
????????????????int?i?=?indexFor(e.hash,?newCapacity);
????????????????e.next?=?newTable[i];
????????????????newTable[i]?=?e;
????????????????e?=?next;
????????????}
????????}
????}
1.1 擴(kuò)容造成死循環(huán)分析過程
hash算法為簡單的用key mod鏈表的大小。
最開始hash表size=2,key=3,7,5,則都在table[1]中。
然后進(jìn)行resize,使size變成4。





newTable[3]=e?---->?newTable[3]=3
e=next?---->?e=7

e=7
next=e.next?---->?next=3【從主存中取值】
e.next=newTable[3]?---->?e.next=3【從主存中取值】
newTable[3]=e?---->?newTable[3]=7
e=next?---->?e=3

e=3
next=e.next?---->?next=null
e.next=newTable[3]?---->?e.next=7?即:3.next=7
newTable[3]=e?---->?newTable[3]=3
e=next?---->?e=null

1.2 擴(kuò)容造成數(shù)據(jù)丟失分析過程




e=5
next=e.next?---->?next=null,從主存中取值
e.next=newTable[1]?---->?e.next=5,從主存中取值
newTable[1]=e?---->?newTable[1]=5
e=next?---->?e=null

2.jdk1.8中HashMap
final?V?putVal(int?hash,?K?key,?V?value,?boolean?onlyIfAbsent,
???????????????????boolean?evict)?{
????????Node[]?tab;?Node ?p;?int?n,?i;
????????if?((tab?=?table)?==?null?||?(n?=?tab.length)?==?0)
????????????n?=?(tab?=?resize()).length;
????????if?((p?=?tab[i?=?(n?-?1)?&?hash])?==?null)?//?如果沒有hash碰撞則直接插入元素
????????????tab[i]?=?newNode(hash,?key,?value,?null);
????????else?{
????????????Node?e;?K?k;
????????????if?(p.hash?==?hash?&&
????????????????((k?=?p.key)?==?key?||?(key?!=?null?&&?key.equals(k))))
????????????????e?=?p;
????????????else?if?(p?instanceof?TreeNode)
????????????????e?=?((TreeNode)p).putTreeVal(this,?tab,?hash,?key,?value);
????????????else?{
????????????????for?(int?binCount?=?0;?;?++binCount)?{
????????????????????if?((e?=?p.next)?==?null)?{
????????????????????????p.next?=?newNode(hash,?key,?value,?null);
????????????????????????if?(binCount?>=?TREEIFY_THRESHOLD?-?1)?//?-1?for?1st
????????????????????????????treeifyBin(tab,?hash);
????????????????????????break;
????????????????????}
????????????????????if?(e.hash?==?hash?&&
????????????????????????((k?=?e.key)?==?key?||?(key?!=?null?&&?key.equals(k))))
????????????????????????break;
????????????????????p?=?e;
????????????????}
????????????}
????????????if?(e?!=?null)?{?//?existing?mapping?for?key
????????????????V?oldValue?=?e.value;
????????????????if?(!onlyIfAbsent?||?oldValue?==?null)
????????????????????e.value?=?value;
????????????????afterNodeAccess(e);
????????????????return?oldValue;
????????????}
????????}
????????++modCount;
????????if?(++size?>?threshold)
????????????resize();
????????afterNodeInsertion(evict);
????????return?null;
????}
總結(jié)
在jdk1.7中,在多線程環(huán)境下,擴(kuò)容時會造成環(huán)形鏈或數(shù)據(jù)丟失。
在jdk1.8中,在多線程環(huán)境下,會發(fā)生數(shù)據(jù)覆蓋的情況。
推薦閱讀:
【19期】為什么Java線程沒有Running狀態(tài)?
【18期】Java序列化與反序列化三連問:是什么?為什么要?如何做?
【17期】什么情況用ArrayList or LinkedList呢?
微信掃描二維碼,關(guān)注我的公眾號
朕已閱?
評論
圖片
表情

