簡體   English   中英

java 會以一致的方式對 ConcurrentHashMap 的值求和嗎?

[英]Will java streams sum values of a ConcurrentHashMap in an consistent manner?

我有一個 concurrentHashMap 實例,一些線程向其中添加了條目。 這些值是整數。

同時,其他線程希望檢索映射中所有值的總和。 我希望這些線程看到一致的值。 但是,它們不必總是看到最新的值。

以下代碼線程安全嗎?

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class MyClass {
    private Map<Integer, Integer> values = new ConcurrentHashMap<>();

    public void addValue(Integer key, int value){
        values.put(key, value);
    }

    public long sumOfValues(){
        return values
                .values()
                .stream()
                .mapToInt(Integer::intValue)
                .sum();
    }
}

求和運算會在一組一致的值上計算嗎?

當 sum 運算發生時,對 put() 的調用會被阻塞嗎?

當然我可以自己同步訪問,甚至可以拆分讀寫鎖以允許並發讀訪問和同步寫訪問,但是我很好奇在使用 concurrentHashMap 作為集合實現時是否有必要。

文檔說明了ConcurrentHashMapkeySet()entrySet()視圖的迭代器和拆分器弱一致。

弱一致特征為

  • 他們可以與其他操作同時進行
  • 他們永遠不會拋出 ConcurrentModificationException
  • 它們保證遍歷元素,因為它們在構造時就存在過一次,並且可能(但不保證)反映構造后的任何修改。

所以...

以下代碼線程安全嗎?

是的,狹義上沒有 ConcurrentModificationException 或 HashMap 的內部不一致。

求和運算會在一組一致的值上計算嗎?

在弱一致集上

當 sum 運算發生時,對 put() 的調用會被阻塞嗎?

ConcurrentHashMap的要點是條目盡可能相互獨立。 對整個地圖沒有一致的看法。 事實上,即使size也不會返回一個非常有用的值。

如果您需要同時查詢總和,一種解決方案是編寫一個包裝類來維護地圖的狀態和總和,使用LongAdder原子地維護總和。

import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.LongAdder;

public class MapSum {
    private final ConcurrentMap<Integer, Integer> map = new ConcurrentHashMap<>();
    private final LongAdder sum = new LongAdder();

    public Integer get(Integer k) {
        return map.get(k);
    }

    public Integer put(Integer k, Integer v) {
        Integer[] out = new Integer[1];
        map.compute(k, (_k, old) -> {
            out[0] = old;
            // cast to long to avoid overflow
            sum.add((long) v - (old != null ? old : 0));
            return v;
        });
        return out[0];
    }

    public Integer remove(Integer k) {
        Integer[] out = new Integer[1];
        map.compute(k, (_k, old) -> {
            out[0] = old;
            // cast to long to avoid overflow; -Integer.MIN_VALUE == Integer.MIN_VALUE
            if(old != null) { sum.add(- (long) old); }
            return null;
        });
        return out[0];
    }

    public long sum() {
        return sum.sum();
    }
}

這具有在 O(1) 而不是 O( n ) 時間內查詢總和的額外好處。 如果願意,您可以添加更多Map方法,甚至可以實現Map<Integer, Integer> - 當您以任何方式更改地圖內容時,請小心維護總和。

暫無
暫無

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

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