我有以下定义的
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调用来将其视为线程安全?
答案 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
放入映射中,并使用原子计算/替换/合并操作更新它们。