哈嘍,你們好,我是老王,歡迎來到 Java 面試突擊,咱們今天來開始第 6 期的內容。
本期的問題是:HashMap 爲什會致使 CPU 運行 100%?這是一個比較常見的經典問題了,可是有不少人讀者朋友給我反饋,尼瑪,看文章根本看不懂啊?Sun 公司都不知道這個問題的緣由吧?不,是 Oracle 公司都不知道這個問題的緣由吧?面試官怕也不知道這個的答案吧?
咳咳,做爲一個很正經的面試官,我以爲這個問題一點都不重要,重要的是你不知道答案啊。好的,下一位面試者請進,您先回去等通知吧。
爲了不這種尷尬的事情發生,今天咱們來好好聊一下這個問題,畢竟技能再手,才能吊打面試官不是?
正文面試
這個問題相關的知識點,有如下幾個:
H```
ashMap 的底層數據結構是什麼?
什麼是哈希碰撞?如何該解決這個問題?
什麼是擴展因子?它有什麼用?
還有對 HashMap 源碼的理解,爲何 HashMap 會致使死循環?數組
視頻版答案 視頻內容以下: 圖文答案 1.HashMap 的底層數據結構 先來講 HashMap 的底層數據結構,看過 HashMap 的源碼咱們就會發現,JDK 1.7 和 JDK 1.8 HashMap 的組成是不一樣的,JDK 1.7 HashMap 的組成是數組 + 鏈表的形式,而 JDK 1.8 新增了紅黑樹的數據結構,當 HashMap 中的鏈表長度大於 8 時,鏈表結構就會轉換爲紅黑樹,以下圖所示: ![](https://s4.51cto.com/images/blog/202007/31/4e006f8b3e8d00bc4d156443365473de.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=) 2.哈希碰撞及解決方案 所謂的哈希碰撞指的是不一樣的值,通過哈希以後獲得的值確是相同的,這種狀況就叫作哈希碰撞或哈希衝突。解決哈希碰撞的經常使用方法是:開放定址法和鏈表地址法,而 HashMap 採用的就是鏈表地址法。它的實現原理就是將 HashMap 中相同的哈希值以鏈表的形式存儲起來。 3.擴展因子 擴展因子也叫加載因子或負載因子是 HashMap 中的一個屬性,以下圖所示:假如數組的默認長度爲 10,擴展因子爲 0.5,那麼當數組超過 10*0.5=5 個時,HashMap 就會擴容爲以前容量的兩倍,因此說擴展因子就是用來斷定 HashMap 是否知足擴容條件的。 4.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) 的環形引用,就致使了死循環的產生,以下圖所示: ![](https://s4.51cto.com/images/blog/202007/31/cce05aec85fda2d0a701473f3654477e.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=) HashMap 發生死循環的一個重要緣由是 JDK 1.7 時鏈表的插入是首部倒序插入的,而 JDK 1.8 時已經變成了尾部插入,有人把這個死循環的問題反饋給了 Sun 公司,但它們認爲這不是一個問題,由於 HashMap 自己就是非線程安全的,若是要在多線程使用建議使用 ConcurrentHashMap 替代 HashMap,但面試中這個問題被問的頻率比較高,因此在這裏就特殊說明一下。 小結 HashMap 是非線程安全的,以 JDK 1.7 爲例,當多線程併發擴容時就會出現環形引用的問題,從而致使死循環的出現,一直死循環就會致使 CPU 運行 100%,因此在多線程使用時,咱們須要使用 ConcurrentHashMap 來替代 HashMap,但只有懂得其中的因果關係才能吊打面試官,好了,本節內容到這裏就結束了,咱們下期再見。 上期中獎名單:皮卡皮卡、一步、好好學習、談笑、包子有話要講。 以上中獎的朋友,請加個人微信:GG_Stone 領取獎勵。 【END】 近期熱文 * 面試突擊 005 | Redis 是如何實現高可用的?它的實現方式有哪些? * 面試突擊 004 | 如何排查 Redis 中的慢查詢?視頻實戰篇 * 面試突擊 003 | Redis 如何實現查詢附近的人? * 面試突擊 002 | Redis 是如何處理已過時元素的? * 面試突擊 001 | Redis 如何從海量數據中查詢出某一個 Key? * Java面試詳解(2020版):500+ 面試題和核心知識點詳解 關注下方二維碼,訂閱更多精彩內容 ![](https://s4.51cto.com/images/blog/202007/31/dba489b49a779bc90fe04f2d66b60f44.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)