您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關HashMap為什么會導致CPU100%,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
先來說 HashMap 的底層數據結構,看過 HashMap 的源碼我們就會發現,JDK 1.7 和 JDK 1.8 HashMap 的組成是不同的,JDK 1.7 HashMap 的組成是數組 + 鏈表的形式,而 JDK 1.8 新增了紅黑樹的數據結構,當 HashMap 中的鏈表長度大于 8 時,鏈表結構就會轉換為紅黑樹,如下圖所示:
所謂的哈希碰撞指的是不同的值,經過哈希之后得到的值確是相同的,這種情況就叫做哈希碰撞或哈希沖突。解決哈希碰撞的常用方法是:開放定址法和鏈表地址法,而 HashMap 采用的就是鏈表地址法。它的實現原理就是將 HashMap 中相同的哈希值以鏈表的形式存儲起來。
擴展因子也叫加載因子或負載因子是 HashMap 中的一個屬性,如下圖所示:假如數組的默認長度為 10,擴展因子為 0.5,那么當數組超過 10*0.5=5 個時,HashMap 就會擴容為之前容量的兩倍,所以說擴展因子就是用來判定 HashMap 是否滿足擴容條件的。
HashMap 導致 CPU 100% 的原因就是因為 HashMap 死循環導致的,那 HashMap 是如何造成死循環的?接下來我們一起來看。
以 JDK 1.7 為例,假設 HashMap 的默認大小為 2,HashMap 本身中有一個鍵值 key(5),我們再使用兩個線程:t1 添加 key(3),t2 添加 key(7),首先兩個線程先把 key(3) 和 key(7) 都添加到 HashMap 中,此時因為 HashMap 的長度不夠用了就會進行擴容操作,然后這時線程 t1 在執行到 Entry<K,V> next = e.next; 時,交出了 CPU 的使用權,源代碼如下:
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; // 線程一執行此處
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;
}
}
}
那么此時線程 t1 中的 e 指向了 key(3),而 next 指向了 key(7) ;之后線程 t2 重新 rehash 之后鏈表的順序被反轉,鏈表的位置變成了 key(5) -> key(7) -> key(3),其中 “->” 用來表示下一個元素,當 t1 重新獲得執行權之后,先執行 newTalbe[i] = e 把 key(3) 的 next 設置為 key(7),而下次循環時查詢到 key(7) 的 e.next 為 key(3),于是就形 成了 key(3) 和 key(7) 的環形引用,就導致了死循環的產生,如下圖所示:
HashMap 發生死循環的一個重要原因是 JDK 1.7 時鏈表的插入是首部倒序插入的,而 JDK 1.8 時已經變成了尾部插入,有人把這個死循環的問題反饋給了 Sun 公司,但它們認為這不是一個問題,因為 HashMap 本身就是非線程安全的,如果要在多線程使用建議使用 ConcurrentHashMap 替代 HashMap,但面試中這個問題被問的頻率比較高,所以在這里就特殊說明一下。
HashMap 是非線程安全的,以 JDK 1.7 為例,當多線程并發擴容時就會出現環形引用的問題,從而導致死循環的出現,一直死循環就會導致 CPU 運行 100%。
關于HashMap為什么會導致CPU100%就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。