繁体   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