避免在ConcurrentHashMap.computeIfAbsent()中进行递归
有時我們會提供糟糕的建議。 就像該文章中有關如何將Java 8用于緩存的功能性方法來計算斐波那契數的文章一樣 。 正如我們的讀者之一馬蒂亞斯(Matthias)在評論中注意到的那樣 ,提出的算法可能永遠不會停止。 考慮以下程序:
它將至少在以下Java版本上無限期運行:
C:\Users\Lukas>java -version java version "1.8.0_40-ea" Java(TM) SE Runtime Environment (build 1.8.0_40-ea-b23) Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode)這當然是“功能” 。 ConcurrentHashMap.computeIfAbsent() Javadoc讀取:
如果指定的鍵尚未與某個值關聯,則嘗試使用給定的映射函數計算其值,除非為null,否則將其輸入此映射。 整個方法調用是原子執行的,因此每個鍵最多可應用一次該功能。 在進行計算時,可能會阻止其他線程對此映射進行的某些嘗試的更新操作,因此計算應簡短而簡單, 并且不得嘗試更新此映射的任何其他映射 。
盡管并非出于相同的并發原因,“不得”的措辭是明確的合同,我的算法違反了該合同。
Javadoc還讀取:
拋出:
IllegalStateException-如果計算可檢測到嘗試對此地圖進行遞歸更新,否則將永遠無法完成
但是不會拋出該異常。 也沒有任何ConcurrentModificationException。 相反,該程序永遠不會停止。
解決此具體問題的最簡單的使用現場解決方案是不使用ConcurrentHashMap,而僅使用HashMap:
static Map<Integer, Integer> cache = new HashMap<>();覆蓋超類型合約的子類型
Map.computeIfAbsent() HashMap.computeIfAbsent()或Map.computeIfAbsent() Javadoc禁止這種遞歸計算,這當然是荒謬的,因為緩存的類型是Map<Integer, Integer> ,而不是ConcurrentHashMap<Integer, Integer> 。 子類型徹底重新定義超級類型協定是非常危險的( Set vs. SortedSet是問候)。 因此,在超級類型中也應禁止執行此類遞歸。
進一步參考
盡管合同問題只是人們的看法,但停頓問題顯然是一個漏洞。 我還在Stack Overflow上記錄了此問題,在該問題中 , Ben Manes提供了一個有趣的答案,導致了先前的錯誤報告(截至2015年初尚未解決):
- https://bugs.openjdk.java.net/browse/JDK-8062841
我自己的報告(可能是上述報告的副本)也很快被接受,原因是:
- https://bugs.openjdk.java.net/browse/JDK-8074374
Oracle正在研究此問題時,請記住:
切勿在ConcurrentHashMap.computeIfAbsent()方法內部進行遞歸。 如果您正在實現集合,并且認為編寫一個可能無限的循環是個好主意,請再考慮一下,然后閱讀我們的文章:
無限循環。 或者:可能出錯的任何東西都可以 )
墨菲總是對的。
翻譯自: https://www.javacodegeeks.com/2015/03/avoid-recursion-in-concurrenthashmap-computeifabsent.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的避免在ConcurrentHashMap.computeIfAbsent()中进行递归的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 期货如何设置止损?
- 下一篇: JPA和Hibernate级联类型的初学