ins*_*n-e 12 java concurrency multithreading concurrenthashmap concurrent-programming
这可能是一个重复的问题,但我在一本关于并发的书中找到了这部分代码.这据说是线程安全的:
ConcurrentHashMap<String, Integer> counts = new ...;
private void countThing(String thing) {
while (true) {
Integer currentCount = counts.get(thing);
if (currentCount == null) {
if (counts.putIfAbsent(thing, 1) == null)
break;
} else if (counts.replace(thing, currentCount, currentCount + 1)) {
break;
}
}
}
Run Code Online (Sandbox Code Playgroud)
从我的(并发初学者)的角度来看,线程t1和线程t2都可以读取currentCount = 1
.然后两个线程都可以将地图的值更改为2.有人可以解释我代码是否正常?
ysh*_*vit 11
诀窍在于replace(K key, V oldValue, V newValue)
为您提供原子性.从文档(强调我的):
仅当前映射到给定值时才替换键的条目.......动作以原子方式执行.
关键词是"原子地".在内部replace
,"检查旧值是否符合我们的预期,并且只有它是替换它"发生在一个单一的工作块中,没有其他线程可以与它交错.实现取决于它需要做什么同步以确保它提供这种原子性.
因此,两个线程都不currentAction == 1
能从replace
函数中看到.其中一个将其视为1,因此其调用replace
将返回true.另一个将它看作2(因为第一次调用),因此返回false - 并循环返回再次尝试,这次使用新的值currentAction == 2
.
当然,可能是第三个线程同时将currentAction更新为3,在这种情况下,第二个线程将继续尝试,直到它足够幸运,没有任何人跳过它.
有人可以解释我代码是否合适?
除了yshavit的答案,你可以避免使用compute
Java 8中添加的循环来编写自己的循环.
ConcurrentMap<String, Integer> counts = new ...;
private void countThing(String thing) {
counts.compute(thing, (k, prev) -> prev == null ? 1 : 1 + prev);
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
323 次 |
最近记录: |