Hashmap頭插法死循環


  先來看一看老版本HashMap擴容代碼:

void resize(int newCapacity)
{
    Entry[] oldTable = table;
    int oldCapacity = oldTable.length;
    ......
    //創建一個新的Hash Table
    Entry[] newTable = new Entry[newCapacity];
    //將Old Hash Table上的數據遷移到New Hash Table上
    transfer(newTable);
    table = newTable;
    threshold = (int)(newCapacity * loadFactor);
}

 

   其中,重點在於transfer():

void transfer(Entry[] newTable)
{
  //復制一個原數組src,Entry是一個靜態內部類,有K,V,next三個成員變量 Entry[] src
= table;
  //數組新容量
int newCapacity = newTable.length;// 從OldTable里摘一個元素出來,然后放到NewTable中 for (int j = 0; j < src.length; j++) { Entry<K,V> e = src[j];//取出原數組一個元素 if (e != null) {//判斷原數組該位置有元素 src[j] = null;//原數組位置置為空 do {//對原數組某一位置下的一串元素進行操作 Entry<K,V> next = e.next;//next是當前元素下一個 int i = indexFor(e.hash, newCapacity);//i是元素在新數組的位置 e.next = newTable[i];//此處體現了頭插法,當前元素的下一個是新數組的頭元素 newTable[i] = e;//將原數組元素加入新數組 e = next;//遍歷到原數組某一位置下的一串元素的下一個
      } while (e != null);
    }
  }
}

 

   接下來圖示單線程情況下,do循環內的情況:

  初始:當前數組容量為2,有三個元素3、7、5,此處的hash算法是簡化處理(對容量取模)。因此,3、7、5都在數組索引1對應的鏈表上。

  擴容新容量為2*2=4。

  第一步:當前Entry e對應3,next對應7,新位置i為3,然后將3插入新數組對應位置。

  第二步:當前Entry e對應7,next對應5,新位置i為3,然后將新數組對應索引處的元素3添加到7的尾巴后(頭插),然后將7插入新數組對應位置。

  第三步:當前Entry e對應5,next對應null,新位置i為1, 然后將5插入新數組對應位置。

 

 

   接下來圖示多線程情況下死循環場景:初始條件相同。

  如果有兩個線程:

    線程一執行到 Entry<K,V> next = e.next; 便掛起了,即此時Entry e是3,next是7,3是在7前面的。

    線程二執行完成。

  此時如下圖所示,線程一的3的next是7,而線程二的7的next是3。(此處是Entry里的next成員變量,在多個線程中相同Entry不沖突)。此時可以看出出現了死循環問題。

 

 

   如果此時線程一繼續往下執行:

   第一步:當前Entry e對應3,next對應7,新位置i為3,然后將3插入新數組對應位置。

   第二步:當前Entry e對應7,next對應3(單線程情況下是5),新位置i為3,然后將7插入新數組對應位置。

   第三步:當前Entry e對應3,next對應7,此處死循環,永遠不會跳出while循環。

 

 

 

總結歸納:多線程情況下,使用頭插法會導致鏈表節點之間的關系混亂,出現倒排現象,例如原本3->7->5變成7->3,其他線程此時再進行擴容是會出現死循環。 

 單線程

0   

1        3 ->7 ->5

          e     next

     e next

       e  next=null   

0

1    5

2

3     7  -> 3    

0   

1        3 ->7 ->5

          e     next  線程池一中斷

線程二執行完

0

1    5

2

3     7  -> 3    

線程一繼續

    出現死循環問題

 


免責聲明!

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



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