【HashMap並發修改異常】


我是🌟廖志偉🌟,一名🌕Java開發工程師🌕、📝Java領域優質創作者📝、🎉CSDN博客專家🎉、🌹幕后大佬社區創始人🌹。擁有多年一線研發經驗,研究過各種常見框架中間件的底層源碼,對於大型分布式微服務、三高架構(高性能高並發高可用)有過實踐架構經驗。

🍊博主:java_wxid
🍊博主:Java廖志偉
🍊社區:幕后大佬



本文的大概內容:

HashMap並發修改異常


HashMap實際使用過程中會出現一些線程安全問題,在JDK1.7中,當並發執行擴容操作時會造成環形鏈和數據丟失的情況,開多個線程不斷進行put操作,rehash的時候,舊鏈表遷移新鏈表的時候,如果在新表的數組索引位置相同,則鏈表元素會倒置(就是因為頭插)所以最后的結果打亂了插入的順序,就可能發生環形鏈和數據丟失的問題,引起死循環,導致CPU利用率接近100%。在jdk1.8中對HashMap進行了優化,發生hash碰撞,不再采用頭插法方式,而是直接插入鏈表尾部,因此不會出現環形鏈表的情況,但是在多線程環境下,會發生數據覆蓋的情況,如果沒有hash碰撞的時候,它會直接插入元素。如果線程A和線程B同時進行put操作,剛好這兩條不同的數據hash值一樣,並且該位置數據為null,線程A進入后還未進行數據插入時掛起,而線程B正常執行,從而正常插入數據,然后線程A獲取CPU時間片,此時線程A不用再進行hash判斷了,線程A會把線程B插入的數據給覆蓋,導致數據發生覆蓋的情況,發生線程不安全。可參考【HashMap擴容機制】

實際的故障現象:java.util.ConcurrentModificationException並發修改異常。導致原因:並發爭取修改導致,一個線程正在寫,一個線程過來爭搶,導致線程寫的過程被其他線程打斷,導致數據不一致。

使用HashTable

HashTable是線程安全的,只不過實現代價卻太大了,簡單粗暴,get/put所有相關操作都是synchronized的,這相當於給整個哈希表加了一把大鎖。多線程訪問時候,只要有一個線程訪問或操作該對象,那其他線程只能阻塞,相當於將所有的操作串行化,在競爭激烈的並發場景中性能就會非常差。

使用工具類

線程同步:Map<String,String> hashMap = Collections.synchronizedMap(new HashMap<>());

和Hashtable一樣,實現上在操作HashMap時自動添加了synchronized來實現線程同步,都對整個map進行同步,在性能以及安全性方面不如ConcurrentHashMap。

使用寫時復制(CopyOnWrite)

往一個容器里面加元素的時候,不直接往當前容器添加,而是先將當前容器的元素復制出來放到一個新的容器中,然后新的元素添加元素,添加完之后,再將原來容器的引用指向新的容器,這樣就可以對它進行並發的讀,不需要加鎖,因為當前容器不添加任何元素。利用了讀寫分離的思想,讀和寫是不同的容器。

會有內存占用問題,在復制的時候只是復制容器里的引用,只是在寫的時候會創建新對象添加到新容器里,而舊容器的對象還在使用,所以有兩份對象內存。

會有數據一致性問題,CopyOnWrite容器只能保證數據的最終一致性,不能保證數據的實時一致性。

使用ConcurrentHashMap

為了應對hashmap在並發環境下不安全問題可以使用,ConcurrentHashMap大量的利用了volatile,CAS等技術來減少鎖競爭對於性能的影響。在JDK1.7版本中ConcurrentHashMap避免了對全局加鎖,改成了局部加鎖(分段鎖),分段鎖技術,將數據分成一段一段的存儲,然后給每一段數據配一把鎖,當一個線程占用鎖訪問其中一個段數據的時候,其他段的數據也能被其他線程訪問,能夠實現真正的並發訪問。不過這種結構的帶來的副作用是Hash的過程要比普通的HashMap要長。

所以在JDK1.8版本中CurrentHashMap內部中的value使用volatile修飾,保證並發的可見性以及禁止指令重排,只不過volatile不保證原子性,使用為了確保原子性,采用CAS(比較交換)這種樂觀鎖來解決。

fast-fail機制

在系統設計中,快速失效系統一種可以立即報告任何可能表明故障的情況的系統。快速失效系統通常設計用於停止正常操作,而不是試圖繼續可能存在缺陷的過程。這種設計通常會在操作中的多個點檢查系統的狀態,因此可以及早檢測到任何故障。快速失敗模塊的職責是檢測錯誤,然后讓系統的下一個最高級別處理錯誤。其實就是在做系統設計的時候先考慮異常情況,一旦發生異常,直接停止並上報。

以下的代碼是一個對兩個整數做除法的方法,在fast_fail_method方法中,我們對被除數做了個簡單的檢查,如果其值為0,那么就直接拋出一個異常,並明確提示異常原因。這其實就是fail-fast理念的實際應用。

public int fast_fail_method(int arg1,int arg2){
    if(arg2 == 0){
        throw new RuntimeException("can't be zero");
    }
    return arg1/arg2;
}

在Java集合類中很多地方都用到了該機制進行設計,一旦使用不當,觸發fail-fast機制設計的代碼,就會發生非預期情況。我們通常說的Java中的fail-fast機制,默認指的是Java集合的一種錯誤檢測機制。當多個線程對部分集合進行結構上的改變的操作時,有可能會觸發該機制時,之后就會拋出並發修改異常ConcurrentModificationException。當然如果不在多線程環境下,如果在foreach遍歷的時候使用add/remove方法,也可能會拋出該異常。

之所以會拋出ConcurrentModificationException異常,是因為我們的代碼中使用了增強for循環,而在增強for循環中,集合遍歷是通過iterator進行的,但是元素的add/remove卻是直接使用的集合類自己的方法。這就導致iterator在遍歷的時候,會發現有一個元素在自己不知不覺的情況下就被刪除/添加了,就會拋出一個異常,用來提示可能發生了並發修改!所以,在使用Java的集合類的時候,如果發生ConcurrentModificationException,優先考慮fail-fast有關的情況,實際上這可能並沒有真的發生並發,只是Iterator使用了fail-fast的保護機制,只要他發現有某一次修改是未經過自己進行的,那么就會拋出異常。


總結

以上就是今天要講的內容,還希望各位讀者大大能夠在評論區積極參與討論,給文章提出一些寶貴的意見或者建議📝,合理的內容,我會采納更新博文,重新分享給大家。

🙏四連 關注🔎點贊👍收藏⭐️留言📝

感謝大家的支持,用心寫博文分享給大家,你的支持(🔎點贊👍收藏⭐️留言📝)是對我創作的最大幫助。
🍊微信公眾號:南北踏塵
🍊主頁地址:java_wxid
🍊社區地址:幕后大佬

給讀者大大的話

我本身是一個很普通的程序員,放在人堆里,除了與生俱來的🌹盛世美顏🌹、所剩不多的發量,就剩下180的大高個了。就是我這樣的一個人,默默堅持寫博文也有好多年了,有句老話說的好,🌕牛逼之前都是傻逼式的堅持🌕。希望自己可以通過大量的作品,時間的積累,個人魅力、運氣和時機,可以打造屬於自己的🌟技術影響力🌟。同時也希望自己可以成為一個🎄懂技術🎄,🎄懂業務🎄,🎄懂管理🎄的綜合型人才,作為項目架構路線的總設計師,掌控全局的🌕團隊大腦🌕,技術團隊中的🍊絕對核心🍊是我未來幾年不斷前進的目標。


提示:以下都是資源分享,求個一鍵三連。

面試資料

福利大放送,🎉歡迎關注🔎點贊👍收藏⭐️留言📝,拜托了🙏,這對我真的很重要。
點擊:面試資料
提取碼:2021

200套PPT模板

福利大放送,🎉歡迎關注🔎點贊👍收藏⭐️留言📝,拜托了🙏,這對我真的很重要。
點擊:200套PPT模板
提取碼:2021

提問的智慧

福利大放送,🎉歡迎關注🔎點贊👍收藏⭐️留言📝,拜托了🙏,這對我真的很重要。
點擊:提問的智慧
提取碼:2021

Java開發學習路線

名稱 鏈接
JavaSE 點擊: JavaSE
MySQL專欄 點擊: MySQL專欄
JDBC專欄 點擊: JDBC專欄
MyBatis專欄 點擊: MyBatis專欄
Web專欄 點擊: Web專欄
Spring專欄 點擊: Spring專欄
SpringMVC專欄 點擊: SpringMVC專欄
SpringBoot專欄 點擊: SpringBoot專欄
SpringCould專欄 點擊: SpringCould專欄
Redis專欄 點擊: Redis專欄
Linux專欄 點擊: Linux專欄
Maven3專欄 點擊: Maven3專欄
Spring Security5專欄 點擊: Spring Security5專欄
更多專欄 更多專欄,請到 java_wxid主頁 查看

P5學習路線圖
p5學習路線圖P6學習路線圖
P6學習路線圖P7學習路線圖
P7學習路線圖P8學習路線圖
P8學習路線圖

以上四張圖詳細介紹了作為Java開發工作者所需要具備的知識技能,同學們學廢了嘛,有想法系統學習的同學可以私聊我,🎉歡迎關注🔎點贊👍收藏⭐️留言📝。
🍊博主:java_wxid
🍊博主:Java廖志偉
🍊社區:幕后大佬


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM