1. 程式人生 > >HashMap的死迴圈解析

HashMap的死迴圈解析

HashMap死迴圈問題圖解

     在HashMap的陣列真實長度達到閾值後,會呼叫擴容方法:

    void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return
; } Entry[] newTable = new Entry[newCapacity]; transfer(newTable, initHashSeedAsNeeded(newCapacity)); table = newTable; threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1); }

這裡可以看到如果有兩個執行緒A和B,那麼在呼叫transfer方法之前會在各自的執行緒中建立新的陣列,然後進入到transfer方法中將節點轉移,再看transfer方法:

    void transfer(Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;
        for (Entry<K,V> e : table) {
            while(null != e) {
                Entry<K,V> next = e.next;   ------(1if (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),待會會用到,首先假設HashMap中的結構是這樣的:
這裡寫圖片描述
那麼執行緒A如果執行到(1)的位置,那麼e為節點5,next為節點6,這個時候執行緒B開始執行,在自己的擴容數組裡面執行:

e.next = newTable[i];
newTable[i] = e;

這個時候結構圖:
這裡寫圖片描述
然後e=next;
在進入迴圈執行:

e.next = newTable[i];
newTable[i] = e;

這個時候結構為:
這裡寫圖片描述
然後執行緒B執行完畢。執行緒A開始從(1)後面繼續執行,這個時候也是先執行

e.next = newTable[i];
newTable[i] = e;

這裡寫圖片描述
然後e=next;這個時候e是節點6,然後再進入迴圈,執行上面兩行程式後的結構如下:
這裡寫圖片描述
但是這個時候由於6的next是有值的,是節點5,所以再執行e=next;的時候,e不為空,還會進入一次迴圈,在執行將節點插入頭部的操作,所以這個時候的結構圖:
這裡寫圖片描述
可以,看到已經成為了環狀連結串列,當執行get操作的時候就會產生死迴圈。