在ConcurrentHashMap中添加AtomicInteger

时间:2016-11-26 17:27:45

标签: java multithreading concurrency java.util.concurrent concurrenthashmap

我有以下定义的

private ConcurrentMap<Integer, AtomicInteger>  = new ConcurrentHashMap<Integer, AtomicInteger>();

private void add() {
  staffValues.replace(100, staffValues.get(100), new AtomicInteger(staffValues.get(100).addAndGet(200)));
}

经过测试,我得到的值不是预期的,我认为这里存在竞争条件。有没有人知道通过在get函数中包含get调用来将其视为线程安全?

3 个答案:

答案 0 :(得分:2)

您的代码存在一些问题。最大的问题是你忽略了ConcurrentHashMap.replace的返回值:如果替换没有发生(由于另一个线程已并行替换),你只需将作为如果它发生了。这是导致错误结果的主要原因。

我还认为改变AtomicInteger然后立即用不同的AtomicInteger替换它是一个设计错误;即使你能使这个工作,也没有理由。

最后,我认为你不应该两次致电staffValues.get(100)。我认为这不会导致当前代码中的错误 - 您的正确性仅取决于第二次调用返回&#34;更新&#34;结果比第一个,我认为 实际上由ConcurrentHashMap保证 - 但它是脆弱的,微妙的和令人困惑的。通常,当你调用ConcurrentHashMap.replace时,它的第三个参数应该是你用第二个参数计算的。

总的来说,您可以通过不使用AtomicInteger

来简化代码
private ConcurrentMap<Integer, Integer> staffValues = new ConcurrentHashMap<>();

private void add() {
    final Integer prevValue = staffValues.get(100);
    staffValues.replace(100, prevValue, prevValue + 200);
}

或不使用replace(甚至可能不是ConcurrentMap,具体取决于您是否接触过此地图):

private Map<Integer, AtomicInteger> staffValues = new HashMap<>();

private void add() {
    staffValues.get(100).addAndGet(200);
}

答案 1 :(得分:1)

A good way to handle situations like this is using the computeIfAbsent method (not the compute method that @the8472 recommends)

The computeIfAbsent accepts 2 arguments, the key, and a Function<K, V> that will only be called if the existing value is missing. Since a AtomicInteger is thread safe to increment from multiple threads, you can use it easely in the following manner:

staffValues.computeIfAbsent(100, k -> new AtomicInteger(0)).addAndGet(200);

答案 2 :(得分:0)

您无需使用replace()。 AtomicInteger是一个可变值,无论何时想要递增它都不需要替换它。实际上addAndGet已经将它递增到位。

相反,当没有值时,使用compute将默认值(大概为0)放入地图中,否则获得预先存在的值并增加该值。

另一方面,如果您想使用不可变值将Integer个实例而不是AtomicInteger放入映射中,并使用原子计算/替换/合并操作更新它们。