簡體   English   中英

並發頻率計數器 - 並發問題

[英]Concurrent frequency counter - concurrency issue

我想在Java中創建一個並發頻率計數器類。

一旦處理了請求(通過processRequest方法),代碼就會檢查請求的類型(整數),並計算從給定時間開始處理的請求數(按請求的類型分組)。 processRequest方法將由多個線程同時調用。

還有另外兩種方法:

  • clearMap():每隔3小時由一個線程調用並清除整個地圖。
  • getMap():它可以在任何時候由Web服務調用,並返回頻率映射的當前狀態的不可變副本。

請參閱下面我實施該計划的初步計划。

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();
 }

我認為這是一個更好的解決方案有幾個原因:

  1. 問題中的代碼使用盒裝對象,即(v, d) -> v+1實際上是(Long v, Long d) -> Long.valueOf(v.longValue() + 1)

    該代碼會產生額外的垃圾,使用AtomicLong可以避免這種情況。

    這里的代碼只為每個鍵分配一個對象,並且不需要任何額外的分配來遞增計數器,例如,即使計數器達到數百萬,它仍然只是一個對象。

  2. 取消裝箱,添加1,裝箱操作可能比緊密編碼的incrementAndGet()操作稍長,增加了碰撞的可能性,需要在merge方法中重新嘗試。

  3. 代碼“純度”。 使用一個帶有“值”的方法,然后完全被忽略,對我來說似乎是錯誤的。 這是不必要的代碼噪音。

這些當然是我的意見。 您可以自己做出決定,但我認為此代碼以完全線程安全的方式闡明了目的,即增加一個long計數器。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM