?面試官問我HashMap哪里不安全?是尔,我支支吾吾的說了這些...

本文作者: Code皮皮蝦,CSDN互躬、掘金等各大平臺同名播赁,有興趣的小伙伴可以點一波關注??,感謝您的支持吨铸!
?持續(xù)分享大廠面試題?
公眾號:JavaCodes



前言

HashMap在JDK7和JDK8是有了一些不同的行拢,具體體現(xiàn)如下:

  1. JDK7HashMap底層是數(shù)組+鏈表,而JDK8是數(shù)組+鏈表+紅黑樹
  2. JDK7擴容采用頭插法,而JDK8采用尾插法
  3. JDK7的rehash是全部rehash舟奠,而JDK8是部分rehash竭缝。
  4. JDK8對于key的hash值計算相比于JDK7來說有所優(yōu)化。


如果還有興趣的小伙伴可以學習學習我的以下文章沼瘫,寫的十分詳細Lе健!

高頻考題:手寫HashMap

JDK7耿戚、8擴容源碼級詳解

JDK7湿故、8HashMap的get()、put()流程詳解



JDK7 HashMap

JDK7HashMap在多線程環(huán)境下會出現(xiàn)死循環(huán)問題膜蛔。

假如此時A坛猪、B線程同時對一個HashMap進行put操作,且HashMap剛號達到擴容條件需要進行擴容

那么這兩個線程都會取對HahsMap進行擴容(JDK7HashMap擴容調用 resize()方法皂股,而resize()方法中需要調用transfer()方法將舊數(shù)組元素全部rehash到新數(shù)組中去==重點:這里在多線程環(huán)境下就會出現(xiàn)問題==)

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);
}


void transfer(Entry[] newTable, boolean rehash) {
    int newCapacity = newTable.length;
    //對數(shù)組的每一條鏈表遍歷rehash
    for (Entry<K,V> e : table) {
        while(null != e) {
            //保留下一個節(jié)點
            Entry<K,V> next = e.next;
            if (rehash) {
                e.hash = null == e.key ? 0 : hash(e.key);
            }
            //得到對應在新數(shù)組中的索引位置
            int i = indexFor(e.hash, newCapacity);
            
            //尾插法
            e.next = newTable[i];
            newTable[i] = e;
            e = next;
        }
    }
}

我們假設現(xiàn)在有一個鏈表 C——>D,且C墅茉、D擴容后計算的索引位置依然不變,那他么還在同一鏈表中

現(xiàn)在A線程進入到transfer方法拿到C和它的下一個節(jié)點D(Entry<K,V> next = e.next;)后呜呐,A線程被掛起就斤,此時B線程正常走流程將C、D rehash到新的數(shù)組中蘑辑,那么根據(jù)頭插法在新的數(shù)組中是D——>C

B執(zhí)行完之后洋机,A線程繼續(xù)去執(zhí)行

因為A獲取到了 e = C,next = D,所以C可以進行rehash,C進行完后拿到D洋魂,發(fā)現(xiàn)D.next = C,所以D也可以進行rehash绷旗,那么此時因為D——>C,此時會再拿到C,發(fā)現(xiàn)C.next = null副砍,但C不是null刁标,所以C再進行rehash,此時鏈表尾 C——> D ——>C,因為此時e = NULL址晕,所以退出循環(huán),此時出現(xiàn)死循環(huán)顿锰。C——>D——>C谨垃。


==各位可以好好想想這些話或者自己在草稿紙上畫一畫再來看下面的圖!==


圖示演示:

在這里插入圖片描述

==B正常執(zhí)行完成==

在這里插入圖片描述

==A繼續(xù)執(zhí)行==

因為A獲取到了 e = C,next = D,所以C可以進行rehash

在這里插入圖片描述


C進行完后拿到e = D硼控,發(fā)現(xiàn)D.next = C,所以D也可以進行rehash

在這里插入圖片描述


那么此時因為D——>C,此時會再拿到C刘陶,發(fā)現(xiàn)C.next = null,但C不是null牢撼,所以C再進行rehash

在這里插入圖片描述

此時e = NULL匙隔,所以退出循環(huán),此時出現(xiàn)死循環(huán)熏版。C——>D——>C纷责。



JDK8 HashMap

JDK1.8會出現(xiàn)數(shù)據(jù)覆蓋的情況

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}
  • ==第6行代碼==:假設兩個線程A捍掺、B都在進行put操作,并且根據(jù)key計算出的hash值相同再膳,那么得到得索引下標也相同挺勿,當線程A執(zhí)行完第六行代碼后由于時間片耗盡導致被掛起,而線程B得到時間片后在該下標處插入了元素喂柒,完成了正常的插入不瓶,然后線程A獲得時間片,由于之前已經進行了hash碰撞的判斷灾杰,所有此時不會再進行判斷蚊丐,而是直接進行插入,這就導致了線程B插入的數(shù)據(jù)被線程A覆蓋了艳吠,從而線程不安全麦备。
  • ==第38行代碼==++size不安全,還是線程A讲竿、B泥兰,這兩個線程同時進行put操作時,假設當前HashMap的zise大小為10题禀,當線程A執(zhí)行到第38行代碼時鞋诗,從主內存中獲得size的值為10后準備進行+1操作,但是由于時間片耗盡只好讓出CPU迈嘹,線程B快樂的拿到CPU還是從主內存中拿到size的值10進行+1操作削彬,完成了put操作并將size=11寫回主內存,然后線程A再次拿到CPU并繼續(xù)執(zhí)行(此時size的值仍為10)秀仲,當執(zhí)行完put操作后融痛,還是將size=11寫回內存,此時神僵,線程A雁刷、B都執(zhí)行了一次put操作,但是size的值只增加了1保礼,所有說還是由于數(shù)據(jù)覆蓋又導致了線程不安全沛励。





最后

我是 Code皮皮蝦,一個熱愛分享知識的 皮皮蝦愛好者炮障,未來的日子里會不斷更新出對大家有益的博文目派,期待大家的關注!P灿企蹭!

創(chuàng)作不易,如果這篇博文對各位有幫助,希望各位小伙伴可以一鍵三連哦谅摄!徒河,感謝支持,我們下次再見~~~

==分享大綱==

大廠面試題 - 專題 - 簡書 (jianshu.com)

Java從入門到入墳學習路線目錄索引
開源爬蟲實例教程目錄索引


?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末螟凭,一起剝皮案震驚了整個濱河市虚青,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌螺男,老刑警劉巖棒厘,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異下隧,居然都是意外死亡奢人,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門淆院,熙熙樓的掌柜王于貴愁眉苦臉地迎上來何乎,“玉大人,你說我怎么就攤上這事土辩≈Ь龋” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵拷淘,是天一觀的道長各墨。 經常有香客問我,道長启涯,這世上最難降的妖魔是什么贬堵? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮结洼,結果婚禮上黎做,老公的妹妹穿的比我還像新娘。我一直安慰自己松忍,他們只是感情好蒸殿,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鸣峭,像睡著了一般伟桅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上叽掘,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音玖雁,去河邊找鬼更扁。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的浓镜。 我是一名探鬼主播溃列,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼膛薛!你這毒婦竟也來了听隐?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤哄啄,失蹤者是張志新(化名)和其女友劉穎雅任,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體咨跌,經...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡沪么,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了锌半。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片禽车。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖刊殉,靈堂內的尸體忽然破棺而出殉摔,到底是詐尸還是另有隱情,我是刑警寧澤记焊,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布逸月,位于F島的核電站,受9級特大地震影響亚亲,放射性物質發(fā)生泄漏彻采。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一捌归、第九天 我趴在偏房一處隱蔽的房頂上張望肛响。 院中可真熱鬧,春花似錦惜索、人聲如沸特笋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽猎物。三九已至,卻和暖如春角塑,著一層夾襖步出監(jiān)牢的瞬間蔫磨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工圃伶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留堤如,地道東北人蒲列。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像搀罢,于是被迫代替她去往敵國和親蝗岖。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內容