- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在制作一个应用程序,它需要一堆日记条目并计算总和。
当有多个线程调用addToSum()
方法时,下面的方法是线程/并发安全的。我想确保每次调用都能正确更新总数。
如果不安全,请说明我必须做什么来确保线程安全。
我需要同步
get/put 还是有更好的方法?
private ConcurrentHashMap<String, BigDecimal> sumByAccount;
public void addToSum(String account, BigDecimal amount){
BigDecimal newSum = sumByAccount.get(account).add(amount);
sumByAccount.put(account, newSum);
}
非常感谢!
更新:
谢谢大家的回答,我已经知道上面的代码不是线程安全的。
感谢 Vint 建议使用 AtomicReference
作为 synchronize
的替代方案。我以前使用 AtomicInteger
来保存整数和,我想知道 BigDecimal 是否有类似的东西。
两者的优劣是否有定论?
最佳答案
您可以像其他人建议的那样使用同步,但如果想要一个最小阻塞的解决方案,您可以尝试将 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;
}
}
编辑 - 我将对此进行更多解释:
AtomicReference 使用 CAS以原子方式分配单个引用。循环是这样说的。
如果 AtomicReference 中存储的当前字段 == oldVal
[它们在内存中的位置,而不是它们的值] 然后将 AtomicReference 中存储的字段的值替换为 oldVal.add(amount)
。现在,在您调用 newSum.get() 的 for 循环之后的任何时候,它都会有已添加到的 BigDecimal 对象。
您想在此处使用循环,因为有可能两个线程正在尝试添加到同一个 AtomicReference。可能会发生一个线程成功而另一个线程失败的情况,如果发生这种情况,只需使用新的附加值重试。
在中等线程争用情况下,这会是一个更快的实现,在高争用情况下,您最好使用 synchronized
关于java - 如何在 ConcurrentHashMap 线程安全中更新 BigDecimal,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8567596/
我是一名优秀的程序员,十分优秀!