面試阿里當場傻眼,被P8質問:ConcurrentHashMap真的執行緒安全嗎?
沒啥深入實踐的初中級工程師,使用併發工具時,自以為把HashMap改為ConcurrentHashMap,就能完美解決併發。
或者使用寫時複製的CopyOnWriteArrayList,效能更佳呀!
技術言論雖然自由,但面對P8魔鬼面試官時, 你能針對他提問的場景還能做出是否執行緒安全的正確判斷嗎?
我們都知道ConcurrentHashMap是個執行緒安全的雜湊表容器,但它僅保證提供的原子性讀寫操作執行緒安全。
案例
有個含900個元素的Map,現在再補充100個元素進去,這個補充操作由10個執行緒併發進行。 開發人員誤以為使用ConcurrentHashMap就不會有執行緒安全問題,於是不加思索地寫出了下面的程式碼:在每一個執行緒的程式碼邏輯中先通過size方法拿到當前元素數量,計算ConcurrentHashMap目前還需要補充多少元素,並在日誌中輸出了這個值,然後通過putAll方法把缺少的元素新增進去。
為方便觀察問題,我們輸出了這個Map一開始和最後的元素個數。
訪問介面分析日誌輸出可得: - 初始大小900符合預期,還需填充100個元素 - worker13執行緒查詢到當前需要填充的元素為49,還不是100的倍數 - 最後HashMap的總專案數是1549,也不符合填充滿1000的預期
bug 分析
ConcurrentHashMap就像是一個大籃子,現在這個籃子裡有900個桔子,我們期望把這個籃子裝滿1000個桔子,也就是再裝100個桔子。有10個工人來幹這件事兒,大家先後到崗後會計算還需要補多少個桔子進去,最後把桔子裝入籃子。 ConcurrentHashMap這籃子本身,可以確保多個工人在裝東西進去時,不會相互影響干擾,但無法確保工人A看到還需要裝100個桔子但是還未裝時,工人B就看不到籃子中的桔子數量。你往這個籃子裝100個桔子的操作不是原子性的,在別人看來可能會有一個瞬間籃子裡有964個桔子,還需要補36個桔子。
ConcurrentHashMap對外提供能力的限制: - 使用不代表對其的多個操作之間的狀態一致,是沒有其他執行緒在操作它的。如果需要確保需要手動加鎖 - 諸如size、isEmpty和containsValue等聚合方法,在併發下可能會反映ConcurrentHashMap的中間狀態。因此在併發情況下,這些方法的返回值只能用作參考,而不能用於流程控制。顯然,利用size方法計算差異值,是一個流程控制 - 諸如putAll這樣的聚合方法也不能確保原子性,在putAll的過程中去獲取資料可能會獲取到部分資料
解決方案
整段邏輯加鎖:
只有一個執行緒查詢到需補100個元素,其他9個執行緒查詢到無需補,最後Map大小1000
既然使用ConcurrentHashMap還要全程加鎖,還不如使用HashMap呢? 不完全是這樣。
ConcurrentHashMap提供了一些原子性的簡單複合邏輯方法,用好這些方法就可以發揮其威力。這就引申出程式碼中常見的另一個問題:在使用一些類庫提供的高階工具類時,開發人員可能還是按照舊的方式去使用這些新類,因為沒有使用其真實特性,所以無法發揮其威力。
案例
使用Map來統計Key出現次數的場景。 - 使用ConcurrentHashMap來統計,Key的範圍是10 - 使用最多10個併發,迴圈操作1000萬次,每次操作累加隨機的Key - 如果Key不存在的話,首次設定值為1。
show me code:
有了上節經驗,我們這直接鎖住Map,再做 - 判斷 - 讀取現在的累計值 - +1 - 儲存累加後值
這段程式碼在功能上的確毫無沒有問題,但卻無法充分發揮ConcurrentHashMap的效能,優化後:
ConcurrentHashMap的原子性方法computeIfAbsent做複合邏輯操作,判斷K是否存在V,若不存在,則把Lambda執行後結果存入Map作為V,即新建立一個LongAdder物件,最後返回V 因為computeIfAbsent返回的V是LongAdder,是個執行緒安全的累加器,可直接呼叫其increment累加。
這樣在確保執行緒安全的情況下達到極致效能,且程式碼行數驟減。
效能測試
- 使用StopWatch測試兩段程式碼的效能,最後的斷言判斷Map中元素的個數及所有V的和是否符合預期來校驗程式碼正確性
效能測試結果比使用鎖效能提升至少5倍。
computeIfAbsent高效能之道
Java的Unsafe實現的CAS。 它在JVM層確保寫入資料的原子性,比加鎖效率高:
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
Node<K,V> c, Node<K,V> v) {
return U.compareAndSetObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
所以不要以為只要用了ConcurrentHashMap併發工具就是高效能的高併發程式。
辨明 computeIfAbsent、putIfAbsent
- 當Key存在的時候,如果Value獲取比較昂貴的話,putIfAbsent就白白浪費時間在獲取這個昂貴的Value上(這個點特別注意)
- Key不存在的時候,putIfAbsent返回null,小心空指標,而computeIfAbsent返回計算後的值
- 當Key不存在的時候,putIfAbsent允許put null進去,而computeIfAbsent不能,之後進行containsKey查詢是有區別的(當然了,此條針對HashMap,ConcurrentHashMap不允許put null value進去)
文章末尾
歡迎各位大佬進群共同交流學習,我們的交流分享群:1149778920 暗號:CSDN
博主在這裡給大家整理了包括但不限於:JAVA基礎和進階類、Spring、Spring boot、Spring MVC、MyBatis、MySQL、JVM等各種資料有,免費分享給各位進群的小夥伴
相關文章
- ConcurrentHashMap執行緒安全嗎?HashMap執行緒
- 面試被問執行緒池,真香面試執行緒
- ConcurrentHashMap的size方法是執行緒安全的嗎?HashMap執行緒
- Java執行緒安全面試題,你真的瞭解嗎?Java執行緒面試題
- 【Java】ConcurrentHashMap執行緒安全技巧JavaHashMap執行緒
- ConcurrentHashMap一定執行緒安全?HashMap執行緒
- 你會這道阿里多執行緒面試題嗎?阿里執行緒面試題
- 阿里面試官的突擊面試,被問到當場自閉!!!阿里面試
- Java 執行緒安全問題的本質Java執行緒
- 你的單例模式真的是執行緒安全的嗎?單例模式執行緒
- 面試官:Context攜帶資料是執行緒安全的嗎?面試Context執行緒
- 敲開阿里大門的執行緒、多執行緒和執行緒池面試專題阿里執行緒面試
- ConcurrentHashMap執行緒安全機制以及原始碼分析HashMap執行緒原始碼
- 當面試官問執行緒池時,你應該知道些什麼?面試執行緒
- Java多執行緒面試高配問題---多執行緒(3)🧵Java執行緒面試
- 面試官:Java執行緒可以無限建立嗎?面試Java執行緒
- Tomcat+Redis+執行緒池鎖+悲觀鎖+NIO-阿里P8總結三面四輪技術面試TomcatRedis執行緒阿里面試
- 【Java】面試官靈魂拷問:if語句執行完else語句真的不會再執行嗎?Java面試
- 從原始碼分析ConcurrentHashMap執行緒安全和高效的特性原始碼HashMap執行緒
- Flash圖解執行緒池 | 阿里巴巴面試官喜歡問的執行緒池到底是什麼?圖解執行緒阿里面試
- java多執行緒程式設計:你真的瞭解執行緒中斷嗎?Java執行緒程式設計
- Java中執行緒池,你真的會用嗎?Java執行緒
- 03 執行緒安全問題執行緒
- SimpleDateFormat 執行緒安全問題ORM執行緒
- 多執行緒,你覺得你安全了?(執行緒安全問題)執行緒
- 面試官問,Redis 是單執行緒還是多執行緒?我懵了面試Redis執行緒
- 多執行緒面試題執行緒面試題
- BATJ都愛問的多執行緒面試題BAT執行緒面試題
- HashMap為何執行緒不安全?HashMap,HashTable,ConcurrentHashMap對比HashMap執行緒
- Java面試題:SimpleDateFormat是執行緒安全的嗎?使用時應該注意什麼?Java面試題ORM執行緒
- Java多執行緒中執行緒安全與鎖問題Java執行緒
- 【Java面試題】Java面試之多執行緒!Java面試題執行緒
- ArrayList 的執行緒安全問題執行緒
- 深入JAVA執行緒安全問題Java執行緒
- 面試時說Redis是單執行緒的,被噴慘了!面試Redis執行緒
- 你真的瞭解過 ConcurrentHashMap 嗎?HashMap
- 多執行緒面試題1執行緒面試題
- Linux多執行緒面試題Linux執行緒面試題