1. 程式人生 > >resize導致死迴圈的問題

resize導致死迴圈的問題

多執行緒擴容: 這裡我們先把核心程式碼搬出來, 方便檢視   while(null != e) {     Entry<K,V> next = e.next; //第一行     int i = indexFor(e.hash, newCapacity); //第二行     e.next = newTable[i]; //第三行     newTable[i] = e; //第四行
    e = next; //第五行 }   去掉了一些冗餘的程式碼, 層次結構更加清晰了。 第一行:記錄odl hash表中e.next 第二行:rehash計算出陣列的位置(hash表中桶的位置) 第三行:e要插入連結串列的頭部, 所以要先將e.next指向new hash表中的第一個元素 第四行:將e放入到new hash表的頭部 第五行: 轉移e到下一個節點, 繼續迴圈下去   核心程式碼如上所說, 下面就是多執行緒同時put的情況了, 然後同時進入transfer方法中:

假設這裡有兩個執行緒同時執行了put()操作,並進入了transfer()環節

while(null != e) {
    Entry<K,V> next = e.next; //執行緒1執行到這裡被排程掛起了
    e.next = newTable[i];
    newTable[i] = e;
    e = next;
}

那麼現在的狀態為:

從上面的圖我們可以看到,因為執行緒1的 e 指向了 key(3),而 next 指向了 key(7),線上程2 rehash 後,就指向了執行緒2 rehash 後的連結串列。

然後執行緒1被喚醒了:

  1. 執行e.next = newTable[i],於是 key(3)的 next 指向了執行緒1的新 Hash 表,因為新 Hash 表為空,所以e.next = null
  2. 執行newTable[i] = e,所以執行緒1的新 Hash 表第一個元素指向了執行緒2新 Hash 表的 key(3)。好了,e 處理完畢。
  3. 執行e = next,將 e 指向 next,所以新的 e 是 key(7)

然後該執行 key(3)的 next 節點 key(7)了:

  1. 現在的 e 節點是 key(7),首先執行Entry<K,V> next = e.next,那麼 next 就是 key(3)了
  2. 執行e.next = newTable[i],於是key(7) 的 next 就成了 key(3)
  3. 執行newTable[i] = e,那麼執行緒1的新 Hash 表第一個元素變成了 key(7)
  4. 執行e = next,將 e 指向 next,所以新的 e 是 key(3)

這時候的狀態圖為:

然後又該執行 key(7)的 next 節點 key(3)了:

  1. 現在的 e 節點是 key(3),首先執行Entry<K,V> next = e.next,那麼 next 就是 null
  2. 執行e.next = newTable[i],於是key(3) 的 next 就成了 key(7)
  3. 執行newTable[i] = e,那麼執行緒1的新 Hash 表第一個元素變成了 key(3)
  4. 執行e = next,將 e 指向 next,所以新的 e 是 key(7)

這時候的狀態如圖所示:

  很明顯,環形連結串列出現了!!當然,現在還沒有事情,因為下一個節點是 null,所以transfer()就完成了,等put()的其餘過程搞定後,HashMap 的底層實現就是執行緒1的新 Hash 表了。