1. 程式人生 > >併發程式設計二:HashMap怎麼會死鎖呢?

併發程式設計二:HashMap怎麼會死鎖呢?

通過原始碼知道:HashMap的基本資料結構是Entry[] table。每個entry=table[i]都有next屬性,它其實只指向和entry有相同hash值的下一個物件,也就是同一個桶中的下一個資料。

HashMap這個資料結構不是執行緒安全的,幾乎所有人都知道。當我們採用多執行緒進行讀寫的時候必然會存線上程同步的問題。那怎麼會產生死鎖呢?這是我的一個作業。雖然在我的電腦上執行不出來。後來在網上找到了資料:誰能幫忙解釋一下為什麼這個程式會死鎖?

我找到的答案。就是在transfer函式中。我來貼下原始碼。

 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);
        table = newTable;
        threshold = (int)(newCapacity * loadFactor);
    }

    /**
     * Transfers all entries from current table to newTable.
     */
    void transfer(Entry[] newTable) {
        Entry[] src = table;
        int newCapacity = newTable.length;
        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;
                    int i = indexFor(e.hash, newCapacity);
                    e.next = newTable[i];
                    newTable[i] = e;
                    e = next;
                } while (e != null);
            }
        }
    }

resize函式是發生在容量不夠的情況下的。當申請完了新的記憶體空間後,我們要把原來的記憶體空間中的資料轉移到新記憶體空間中去。也就是transfer函式。

假設現在有三個執行緒:T1,T2,T3;在老陣列中的第一個資料也就是e引用的物件,我們稱它為A,在新陣列中的頭兩個資料分別為B和C,B.next=C。

執行緒T1執行到e.next=newTable[i]時執行緒T2執行到next=e.next;然後執行緒t1去繼續

執行,會產生什麼效果呢?A.next=B,B.next=C。e指向的物件是B,newTable[i]=A。然後繼續執行,e.next=newTable[i],也就是B.next=A;同時A.next=B,繼續執行newTable[i] = e,e = next;如果沒有其它執行緒搗亂的話,那麼此時e應該是C啊,可惜只是如果,如果有第三個執行緒T3線上程T1執行e.next = newTable[i]的時候去執行next = e.next;那麼就中途改變了next的值,本來是儲存C的,但是現在成了A了。總結下現在的情況:e引用A,nextTable[i]引用B,A.next=B,B.next=A。現在明白了吧。會一直這麼死鎖下去的。

以上純屬個人見解,如果哪裡有問題,望大神指出。