[英]Concurrent frequency counter - concurrency issue
我想在Java中創建一個並發頻率計數器類。
一旦處理了請求(通過processRequest方法),代碼就會檢查請求的類型(整數),並計算從給定時間開始處理的請求數(按請求的類型分組)。 processRequest方法將由多個線程同時調用。
還有另外兩種方法:
請參閱下面我實施該計划的初步計划。
public class FrequencyCounter {
private final ConcurrentHashMap<Integer,Long> frequencenyMap = new ConcurrentHashMap<>();
public void processRequest(Request request){
frequencenyMap.merge(request.type, 0L, (v, d) -> v+1);
}
public void clearMap(){
frequencenyMap.clear();
}
public Map<Integer,Long> getMap(){
return ImmutableMap.copyOf(frequencenyMap);
}
}
我查看了ConcurrentHashMap的文檔,它告訴我們以原子方式執行merge方法。
因此,一旦clear()方法開始清除映射的散列桶(按照散列桶鎖定),當另一個線程在獲取頻率映射的值並在processRequest方法中遞增其值之間時,不能調用它因為merge方法是以原子方式執行的。
我對嗎? 我的上述計划似乎沒問題嗎?
感謝您的意見。
首先,用AtomicLong
替換Long
。
其次,使用computeIfAbsent
。
private final Map<Integer, AtomicLong> frequencyMap = new ConcurrentHashMap<>();
public void processRequest(Request request){
frequencyMap.computeIfAbsent(request.type, k -> new AtomicLong())
.incrementAndGet();
}
我認為這是一個更好的解決方案有幾個原因:
問題中的代碼使用盒裝對象,即(v, d) -> v+1
實際上是(Long v, Long d) -> Long.valueOf(v.longValue() + 1)
。
該代碼會產生額外的垃圾,使用AtomicLong
可以避免這種情況。
這里的代碼只為每個鍵分配一個對象,並且不需要任何額外的分配來遞增計數器,例如,即使計數器達到數百萬,它仍然只是一個對象。
取消裝箱,添加1,裝箱操作可能比緊密編碼的incrementAndGet()
操作稍長,增加了碰撞的可能性,需要在merge
方法中重新嘗試。
代碼“純度”。 使用一個帶有“值”的方法,然后完全被忽略,對我來說似乎是錯誤的。 這是不必要的代碼噪音。
這些當然是我的意見。 您可以自己做出決定,但我認為此代碼以完全線程安全的方式闡明了目的,即增加一個long
計數器。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.