摘要:前言老王為何半夜慘叫幾行代碼為何導致服務器爆炸說好的線程安全為何還是出問題讓我們一起收看今天的走進正文出現背景說到的出現背景,還得從說起。在跟中,都只是調用的方法,各自都是原子操作,是線程安全的。
前言
老王為何半夜慘叫?幾行代碼為何導致服務器爆炸?說好的線程安全為何還是出問題?讓我們一起收看今天的《走進IT》
正文說到ConcurrentHashMap的出現背景,還得從HashMap說起。
老王是某公司的苦逼Java開發,在互聯網行業中,業務總是迭代得非常快。體現在代碼中的話,就是v1.0的模塊是單線程執行的,這時候使用HashMap是一個不錯的選擇。然而到了v1.5的版本,為了性能考慮,老王覺得把這段代碼改成多線程會更有效率,那么說改就改,然后就愉快的發布上線了。
直到某天晚上,突然收到線上警報,服務器CPU占用100%。這時候驚醒起來一頓排查(百度,谷歌),結果發現原來是HashMap 在并發的環境下進行rehash的時候會造成鏈表的閉環,因此在進行get()操作的時候導致了CPU占用100%。喔,原來HashMap不是線程安全的類,在當前的業務場景中會有問題。那么你這時候又想到了Hashtable,沒錯,這是個線程安全的類,那我先用這個類替換不就行了,一頓commit,push,部署上去了,觀察了一段時間,完美~再也沒出現過類似的問題了。
但是好日子過的并不長久,運營的同事又找上門了,老王啊,XX功能怎么慢了這么多啊?這時候老王就納悶了,我沒改代碼啊?不就上次替換了一個Hashtable,難道這里會有效率的問題?然后又是一頓排查(百度、谷歌),我去,果不其然,原來它線程安全的原因是因為在方法上都加了synchronized,導致我們全部操作都串行化了,難怪這么慢。
經過了2次掉陷阱的經驗,這次的老王已經是非常謹慎的去尋求更好的解決方案了,這時他找到ConcurrentHashMap,而且為了避免再次掉坑他也去提前了解了實現原理,原來這個類是使用了Segment分段鎖,每一個Segment都有自己的鎖,這樣沖突的的范圍就變小了,效率也能提高不少。經過調研發現確實不錯,于是他就放心的把Hashtable給替換掉了,從此運營再也沒來吐槽了,老王又過上了幸福的日子。
經過一段時間緊張的業務開發,此時的項目已經去到了v2.0,之前的ConcurrentHashMap相關的代碼已經被改的面目全非,邏輯也復雜了很多,但項目還是按時順利的上線了。在項目在運行了一段時間以后,居然再次出現線程安全的問題,其根源竟然是ConcurrentHashMap,老王叕陷入了沉思...
拋開復雜的例子,我們用一個多線程并發獲取map中的值并加1,看看最后輸出的數字如何
public class CHMDemo { public static void main(String[] args) throws InterruptedException { ConcurrentHashMapmap = new ConcurrentHashMap (); map.put("key", 1); ExecutorService executorService = Executors.newFixedThreadPool(100); for (int i = 0; i < 1000; i++) { executorService.execute(new Runnable() { @Override public void run() { int key = map.get("key") + 1; //step 1 map.put("key", key);//step 2 } }); } Thread.sleep(3000); //模擬等待執行結束 System.out.println("------" + map.get("key") + "------"); executorService.shutdown(); } }
此時我們看看多次執行輸出的結果
------790------ ------825------ ------875------
通過觀察輸出結果可以發現,這段使用ConcurrentHashMap的代碼,產生了線程安全的問題。我們來分析一下為什么會發生這種情況。在step1跟step2中,都只是調用ConcurrentHashMap的方法,各自都是原子操作,是線程安全的。但是他們組合在一起的時候就會有問題了,A線程在進入方法后,通過map.get("key")拿到key的值,剛把這個值讀取出來還沒有加1的時候,線程B也進來了,那么這導致線程A和線程B拿到的key是一樣的。不僅僅是在
ConcurrentHashMap,在其他的線程安全的容器比如Vector之類的也會出現如此情況,所以在使用這些容器的時候還是不能大意。
1、可以用synchronized
synchronized(this){ //step1 //step2 }
但是用這種方法的話,我們要考慮一下效率的問題,會不會對當前的業務影響很大?
2、用原子類
public class CHMDemo { public static void main(String[] args) throws InterruptedException { ConcurrentHashMapmap = new ConcurrentHashMap (); AtomicInteger integer = new AtomicInteger(1); map.put("key", integer); ExecutorService executorService = Executors.newFixedThreadPool(100); for (int i = 0; i < 1000; i++) { executorService.execute(new Runnable() { @Override public void run() { map.get("key").incrementAndGet(); } }); } Thread.sleep(3000); //模擬等待執行結束 System.out.println("------" + map.get("key") + "------"); executorService.shutdown(); } }
------1001------
此時的輸出結果就正確了,效率上也比第一種解決方案提高很多。
結語人生處處是陷阱,寫代碼也是如此,多思考,多留心。
github:https://github.com/PeppaLittl...
推薦閱讀《使用ConcurrentHashMap一定線程安全嗎?》
《大白話搞懂什么是同步/異步/阻塞/非阻塞》
《Java異常處理最佳實踐及陷阱防范》
《論JVM爆炸的幾種姿勢及自救方法》
有收獲的話,就點個贊吧
關注「深夜里的程序猿」,分享最干的干貨
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/74256.html
摘要:中的使用及在中的沖突方案引言簡稱是在作為的替代選擇新引入的,是包的重要成員。為了解決在頻繁沖突時性能降低的問題,中使用平衡樹來替代鏈表存儲沖突的元素。目前,只有和會在頻繁沖突的情況下使用平衡樹。 java中ConcurrentHashMap的使用及在Java 8中的沖突方案 1、引言 ConcurrentHashMap(簡稱CHM)是在Java 1.5作為Hashtable的替代選擇新...
摘要:線程安全需求分析三個例子都是關于車輛追蹤的。他們使用了不同的方式來保證車輛追蹤類的線程安全性。值得注意的值文檔也是維護線程安全的重要組成部分。 每個例子后面有代碼,大家可以先把代碼粘出來或者開兩個頁面,先過一下例子的代碼,然后一邊看分析一遍看代碼,上下拖動看的話效果不好。 歡迎拍磚和補充。 線程安全需求分析 三個例子都是關于車輛追蹤的。他們使用了不同的方式來保證車輛追蹤類的線程安全性。...
摘要:簡介是的線程安全版本,內部也是使用數組鏈表紅黑樹的結構來存儲元素。相比于同樣線程安全的來說,效率等各方面都有極大地提高。中的關鍵字,內部實現為監視器鎖,主要是通過對象監視器在對象頭中的字段來表明的。 簡介 ConcurrentHashMap是HashMap的線程安全版本,內部也是使用(數組 + 鏈表 + 紅黑樹)的結構來存儲元素。 相比于同樣線程安全的HashTable來說,效率等各方...
摘要:若遇到哈希沖突,則將沖突的值加到鏈表中即可。之后相比于之前的版本,之后在解決哈希沖突時有了較大的變化,當鏈表長度大于閾值默認為時,將鏈表轉化為紅黑樹,以減少搜索時間。有序,唯一紅黑樹自平衡的排序二叉樹。 本文是最最最常見Java面試題總結系列第三周的文章。主要內容: Arraylist 與 LinkedList 異同 ArrayList 與 Vector 區別 HashMap的底層...
摘要:記得,那是一個周末,棧長去某知名互聯網公司面試,好像不到五分鐘,我就被面試官親切地趕出來了,當時我那個尷尬,內心深受打擊。。。 你們可能會想,棧長這么菜的嗎?5分鐘都堅持不了? 本文說起來會有點尷尬,畢竟這是棧長我曾經經歷過的故事。。。 那時候的棧長還真菜,每天寫著 if/ for 及一些簡單的業務邏輯代碼,雖工作有些日子了,但技術水平還停留在剛畢業的起步階段。。。 記得,那是一個周末...
閱讀 1327·2021-10-27 14:14
閱讀 3581·2021-09-29 09:34
閱讀 2486·2019-08-30 15:44
閱讀 1732·2019-08-29 17:13
閱讀 2577·2019-08-29 13:07
閱讀 877·2019-08-26 18:26
閱讀 3350·2019-08-26 13:44
閱讀 3215·2019-08-26 13:37