如何使ConcurrentHashMap线程中的BigDecimal更新安全 [英] How to make updating BigDecimal within ConcurrentHashMap thread safe

查看:1858
本文介绍了如何使ConcurrentHashMap线程中的BigDecimal更新安全的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在创建一个需要一系列日志条目并计算总和的应用程序。



下面的方法是线程/并发安全,当有多线程调用 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。



解决方案

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

  ConcurrentHashMap< String,AtomicReference< BigDecimal>>地图; 

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;
}
}

编辑 - p>

原子参考使用 CAS 原子分配单个参考。



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



你想在这里使用循环,因为它是可能的两个线程试图添加到相同的AtomicReference。因此,可能发生一个线程成功,另一个线程失败,如果发生只是再次尝试新的添加值。



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

I am making an application that takes a bunch of journal entries and calculate sum.

Is below way of doing it is thread/concurrency safe when there are multiple threads calling the addToSum() method. I want to ensure that each call updates the total properly.

If it is not safe, please explain what do I have to do to ensure thread safety.

Do I need to synchronize the get/put or is there a better way?

private ConcurrentHashMap<String, BigDecimal> sumByAccount;

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

Thanks so much!

Update:

Thanks everyone for the answer, I already get that the code above is not thread-safe.

Thanks Vint for suggesting the AtomicReference as an alternative to synchronize. I was using AtomicInteger to hold integer sums before and I was wondering if there are something like that for BigDecimal.

Is the a definitive conclusion on the pro and con of the two?

解决方案

You can use synchronized like the others suggested but if want a minimally blocking solution you can try AtomicReference as a store for the 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;
    }
}

Edit - I'll explain this more:

An AtomicReference uses CAS to atomically assigns a single reference. The loop says this.

If the current field stored in AtomicReference == oldVal [their location in memory, not their value] then replace the value of the field stored in AtomicReference with oldVal.add(amount). Now any time after the for loop you invoke newSum.get() it will have the BigDecimal object that has been added to.

You want to use a loop here because it is possible two threads are trying to add to the same AtomicReference. So it can happen that one thread succeeds and another thread fails, if that happens just try again with the new added value.

With moderate thread contention this would be a faster implementation, with high contention you are better off using synchronized

这篇关于如何使ConcurrentHashMap线程中的BigDecimal更新安全的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆