[英]Updating BigDecimal concurrently within ConcurrentHashMap thread safe
Is the code below thread/concurrency safe when there are multiple threads calling the totalBadRecords()
method from inside other method? 当有多个线程从其他方法中调用totalBadRecords()
方法时,线程/并发下面的代码是否安全? Both map objects parameters to this method are ConcurrentHashMap
. 这个方法的两个map对象参数都是ConcurrentHashMap
。 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 add/put or is there a better way? 我需要同步添加/放置还是有更好的方法?
Do i need to synchronize the get method in TestVO. 我是否需要在TestVO中同步get方法。 TestVO is simple java bean and having getter/setter method. TestVO是简单的java bean,具有getter / setter方法。
Below is my Sample code: 以下是我的示例代码:
public void totalBadRecords(final Map<Integer, TestVO> sourceMap,
final Map<String, String> logMap) {
BigDecimal badCharges = new BigDecimal(0);
boolean badRecordsFound = false;
for (Entry<Integer, TestVO> e : sourceMap.entrySet()) {
if ("Y".equals(e.getValue().getInd()))
badCharges = badCharges.add(e.getValue()
.getAmount());
badRecordsFound = true;
}
if (badRecordsFound)
logMap.put("badRecordsFound:", badCharges.toPlainString());
}
That depends on how your objects are used in your whole application. 这取决于您的对象在整个应用程序中的使用方式。
If each call to totalBadRecords
takes a different sourceMap
and the map (and its content) is not mutated while counting, it's thread-safe: 如果每次调用totalBadRecords
采用不同的sourceMap
并且映射(及其内容)在计数时没有变异,那么它是线程安全的:
badCharges
is a local variable, it can't be shared between thread, and is thus thread-safe (no need to synchronize add
) badCharges
是一个局部变量,它不能在线程之间共享,因此是线程安全的(不需要同步add
) logMap
can be shared between calls to totalBadRecords
: the method put
of ConcurrentHashMap
is already synchronized (or behaves as if it was). logMap
可以呼叫之间共享,以totalBadRecords
:该方法put
的ConcurrentHashMap
已经同步(或行为就好像它是)。 TestVO
are not mutated, the value from getValue()
and getInd()
are always coherent with one other. 如果TestVO
实例没有变异,则getValue()
和getInd()
的值总是相互一致。 sourceMap
is not mutated, so you can iterate over it. sourceMap
没有变异,所以你可以迭代它。 Actually, in this case, you don't need a concurrent map for sourceMap
. 实际上,在这种情况下,您不需要sourceMap
的并发映射。 You could even make it immutable. 你甚至可以让它变得不可变。
If the instances of TestVO
and the sourceMap
can change while counting, then of course you could be counting wrongly. 如果TestVO
和sourceMap
的实例在计数时可以改变,那么当然你可能会错误地计算。
It depends on what you mean by thread-safe. 这取决于线程安全的含义。 And that boils down to what the requirements for this method are. 这可以归结为这种方法的要求 。
At the data structure level, the method will not corrupt any data structures, because the only data structures that could be shared with other threads are ConcurrentHashMap
instances, and they safe against that kind of problem. 在数据结构级别,该方法不会破坏任何数据结构,因为可以与其他线程共享的唯一数据结构是ConcurrentHashMap
实例,并且它们可以安全地抵御这种问题。
The potential thread-safety issue is that iterating a ConcurrentHashMap
is not an atomic operation. 潜在的线程安全问题是迭代ConcurrentHashMap
不是原子操作。 The guarantees for the iterators are such that you are not guaranteed to see all entries in the iteration if the map is updated (eg by another thread) while you are iterating. 对于迭代器的保证是这样的,如果在迭代时更新地图(例如,通过另一个线程),则不能保证看到迭代中的所有条目。 That means that the totalBadRecords
method may not give an accurate count if some other thread modifies the map during the call. 这意味着如果其他线程在调用期间修改了地图,则totalBadRecords
方法可能无法给出准确的计数。 Whether this is a real thread-safety issue depends on whether or not the totalBadRecords
is required to give an accurate result in that circumstance. 这是否是真正的线程安全问题取决于totalBadRecords
是否需要在该情况下给出准确的结果。
If you need to get an accurate count, then you have to (somehow) lock out updates to the sourceMap
while making the totalBadRecords
call. 如果您需要获得准确的计数,那么您必须(以某种方式)在进行totalBadRecords
调用时锁定对sourceMap
更新。 AFAIK, there is no way to do this using (just) the ConcurrentHashMap
API, and I can't think of a way to do it that doesn't make the map a concurrency bottleneck. AFAIK,使用(仅仅) ConcurrentHashMap
API无法做到这一点,我想不出一种方法,它不会使地图成为并发瓶颈。
In fact, if you need to calculate accurate counts, you have to use external locking for (at least) the counting operation, and all operations that could change the outcome of the counting. 实际上,如果需要计算准确的计数,则必须使用外部锁定(至少)计数操作,以及所有可能改变计数结果的操作。 And even that doesn't deal with the possibility that some thread may modify one of the TestVO
objects while you are counting records, and cause the TestVO
to change from "good" to "bad" or vice-versa. 甚至这也不能解决某些线程在计算记录时修改其中一个TestVO
对象的可能性,并导致TestVO
从“好”变为“坏”,反之亦然。
You could use something like the following. 您可以使用以下内容。
That would guarantee you that after a call to the totalBadRecords
method, the String representing the bad charges in the logMap is accurate, you don't have lost updates . 这可以保证在调用totalBadRecords
方法之后,表示logMap中的不良费用的String是准确的,您没有丢失更新 。 Of course a phantom read can always happen, as you do not lock the sourceMap. 当然, 幻象读取总是会发生,因为你没有锁定sourceMap。
private static final String BAD_RECORDS_KEY = "badRecordsFound:";
public void totalBadRecords(final ConcurrentMap<Integer, TestVO> sourceMap,
final ConcurrentMap<String, String> logMap) {
while (true) {
// get the old value that is going to be replaced.
String oldValue = logMap.get(BAD_RECORDS_KEY);
// calculate new value
BigDecimal badCharges = BigDecimal.ZERO;
for (TestVO e : sourceMap.values()) {
if ("Y".equals(e.getInd()))
badCharges = badCharges.add(e.getAmount());
}
final String newValue = badCharges.toPlainString();
// insert into map if there was no mapping before
if (oldValue == null) {
oldValue = logMap.putIfAbsent(BAD_RECORDS_KEY, newValue);
if (oldValue == null) {
oldValue = newValue;
}
}
// replace the entry in the map
if (logMap.replace(BAD_RECORDS_KEY, oldValue, newValue)) {
// update succeeded -> there where no updates to the logMap while calculating the bad charges.
break;
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.