问题描述
我知道 ConcurrentHashMap 是线程安全的,例如 putIfAbsent、Replace 等,但我想知道,像下面这样的代码块是否安全?
I know ConcurrentHashMap is thread-safe e.g.putIfAbsent,Replace etc., but I was wondering, is a block of code like the one below safe?
if (accumulator.containsKey(key)) { //accumulator is a ConcurrentHashMap
accumulator.put(key, accumulator.get(key)+1);
} else {
accumulator.put(key, 0);
}
请记住,一个键的累加器值可能会同时被两个不同的线程询问,这会导致普通 HashMap 出现问题.所以我需要这样的东西吗?
Keep in mind that the accumulator value for a key may be asked by two different threads simultaneously, which would cause a problem in a normal HashMap. So do I need something like this?
ConcurrentHashMap<Integer,Object> locks;
...
locks.putIfAbsent(key,new Object());
synchronized(locks.get(key)) {
if (accumulator.containsKey(key)) {
accumulator.put(key, accumulator.get(key)+1);
} else {
accumulator.put(key, 0);
}
}
推荐答案
if (accumulator.containsKey(key)) { //accumulator is a ConcurrentHashMap
accumulator.put(key, accumulator.get(key)+1);
} else {
accumulator.put(key, 0);
}
不,这段代码不是线程安全的;accumulator.get(key)
可以在 get
和 put
之间更改,也可以在 containsKey 之间添加条目
和 put
.如果您使用的是 Java 8,则可以编写 accumulator.compute(key, (k, v) -> (v == null) ? 0 : v + 1)
,或任何许多等价物,它会起作用.如果你不是,要做的就是写一些像
No, this code is not thread-safe; accumulator.get(key)
can be changed in between the get
and the put
, or the entry can be added between the containsKey
and the put
. If you're in Java 8, you can write accumulator.compute(key, (k, v) -> (v == null) ? 0 : v + 1)
, or any of the many equivalents, and it'll work. If you're not, the thing to do is write something like
while (true) {
Integer old = accumulator.get(key);
if (old == null) {
if (accumulator.putIfAbsent(key, 0) == null) {
// note: it's a little surprising that you want to put 0 in this case,
// are you sure you don't mean 1?
break;
}
} else if (accumulator.replace(key, old, old + 1)) {
break;
}
}
...循环直到它设法进行原子交换.这种循环几乎就是你必须如何做的:它是 AtomicInteger
的工作方式,你要求的是 AtomicInteger
许多键.
...which loops until it manages to make the atomic swap. This sort of loop is pretty much how you have to do it: it's how AtomicInteger
works, and what you're asking for is AtomicInteger
across many keys.
或者,您可以使用库:例如Guava 有 AtomicLongMap
和 ConcurrentHashMultiset
,它们也做类似的事情.
Alternately, you can use a library: e.g. Guava has AtomicLongMap
and ConcurrentHashMultiset
, which also do things like this.
这篇关于ConcurrentHashMap 在增加其值时是否需要同步?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!