如何在ConcurrentHashMap线程安全的情况下更新BigDecimal

Des*_*hou 16 java concurrency thread-safety bigdecimal concurrent-collections

我正在制作一个应用程序,它需要一堆日记帐分录并计算总和.

当有多个线程调用该addToSum()方法时,下面的方法是线程/并发安全.我想确保每次通话都能正确更新总数.

如果不安全,请说明我必须做些什么来确保螺纹安全.

我需要synchronize获取/放置还是有更好的方法?

private ConcurrentHashMap<String, BigDecimal> sumByAccount;

public void addToSum(String account, BigDecimal amount){
    BigDecimal newSum = sumByAccount.get(account).add(amount);
    sumByAccount.put(account, newSum);
}
Run Code Online (Sandbox Code Playgroud)

非常感谢!

更新:

谢谢大家的答案,我已经知道上面的代码不是线程安全的.

感谢Vint建议AtomicReference作为替代品synchronize.之前我AtomicInteger用来保存整数和,我想知道BigDecimal是否有类似的东西.

关于两者的赞成和反对,这是一个明确的结论吗?

Joh*_*int 14

您可以像其他人建议的那样使用synchronized,但如果想要一个最小阻塞解决方案,您可以尝试AtomicReference作为BigDecimal的存储

ConcurrentHashMap<String,AtomicReference<BigDecimal>> map;

public void addToSum(String account, BigDecimal amount) {
    AtomicReference<BigDecimal> newSum = map.get(account);
    for (;;) {
       BigDecimal oldVal = newSum.get();
       if (newSum.compareAndSet(oldVal, oldVal.add(amount)))
            return;
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑 - 我会解释更多:

AtomicReference使用CAS以原子方式分配单个引用.循环说明了这一点.

如果存储在AtomicReference中的当前字段== oldVal [它们在内存中的位置,而不是它们的值]则替换存储在AtomicReference中的字段的值oldVal.add(amount).现在,在for循环之后的任何时候你调用newSum.get()它将具有已添加到的BigDecimal对象.

你想在这里使用一个循环,因为有两个线程可能试图添加到同一个AtomicReference.可能会发生一个线程成功而另一个线程失败,如果发生这种情况,只需再次使用新增值.

如果中等线程争用,这将是一个更快的实现,高争用你最好使用 synchronized