面試阿里當場傻眼,被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執行緒
- Java執行緒安全面試題,你真的瞭解嗎?Java執行緒面試題
- ConcurrentHashMap的size方法是執行緒安全的嗎?HashMap執行緒
- 面試被問執行緒池,真香面試執行緒
- 【Java】ConcurrentHashMap執行緒安全技巧JavaHashMap執行緒
- ConcurrentHashMap一定執行緒安全?HashMap執行緒
- 阿里面試官的突擊面試,被問到當場自閉!!!阿里面試
- Java 執行緒安全問題的本質Java執行緒
- 你的單例模式真的是執行緒安全的嗎?單例模式執行緒
- 你會這道阿里多執行緒面試題嗎?阿里執行緒面試題
- ConcurrentHashMap執行緒安全機制以及原始碼分析HashMap執行緒原始碼
- 面試必問-幾種執行緒安全的Map解析面試執行緒
- Java中執行緒池,你真的會用嗎?Java執行緒
- Android中子執行緒真的不能更新UI嗎?Android執行緒UI
- Android 中子執行緒真的不能更新UI嗎?Android執行緒UI
- java多執行緒程式設計:你真的瞭解執行緒中斷嗎?Java執行緒程式設計
- 敲開阿里大門的執行緒、多執行緒和執行緒池面試專題阿里執行緒面試
- 從原始碼分析ConcurrentHashMap執行緒安全和高效的特性原始碼HashMap執行緒
- SimpleDateFormat 執行緒安全問題ORM執行緒
- java執行緒安全問題Java執行緒
- 03 執行緒安全問題執行緒
- 面試官:Context攜帶資料是執行緒安全的嗎?面試Context執行緒
- 多執行緒,你覺得你安全了?(執行緒安全問題)執行緒
- Java多執行緒中執行緒安全與鎖問題Java執行緒
- HashMap為何執行緒不安全?HashMap,HashTable,ConcurrentHashMap對比HashMap執行緒
- Java多執行緒面試高配問題---多執行緒(3)🧵Java執行緒面試
- ArrayList 的執行緒安全問題執行緒
- 深入JAVA執行緒安全問題Java執行緒
- 當面試官問執行緒池時,你應該知道些什麼?面試執行緒
- 【Java】面試官靈魂拷問:if語句執行完else語句真的不會再執行嗎?Java面試
- 執行緒3--執行緒安全執行緒
- 面試官:Java執行緒可以無限建立嗎?面試Java執行緒
- 執行緒安全和執行緒不安全理解執行緒
- 5招教你實現多執行緒場景下的執行緒安全!執行緒
- 執行緒安全執行緒
- 多執行緒-執行緒安全問題的產生原因分析以及同步程式碼塊的方式解決執行緒安全問題執行緒
- parallelStream中的執行緒安全問題Parallel執行緒
- 所謂的執行緒安全問題執行緒