Java ConcurrentHashMap 操作原子性

标签 java multithreading concurrency 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;
        }
    }
}

从我(并发初学者)的角度来看,线程 t1 和线程 t2 都可以读取 currentCount = 1。然后两个线程都可以将映射的值更改为 2。有人可以解释一下代码是否正确吗?

最佳答案

诀窍在于 replace(K key, V oldValue, V newValue) 为您提供了原子性。来自 the docs (强调我的):

Replaces the entry for a key only if currently mapped to a given value. ... the action is performed atomically.

关键词是“原子地”。在 replace 中,“检查旧值是否符合我们的预期,如果是,则替换它”作为一个单独的工作 block 发生,没有其他线程能够与之交错。执行需要执行任何同步以确保它提供这种原子性。

因此,不可能两个线程都从 replace 函数中看到 currentAction == 1。其中之一会将其视为 1,因此对 replace 的调用将返回 true。另一个会将其视为 2(因为第一次调用),因此返回 false — 并循环返回重试,这次使用 currentAction == 2 的新值。

当然,这可能是第三个线程同时将 currentAction 更新为 3,在这种情况下,第二个线程将继续尝试,直到幸运地没有任何人跳到它前面为止。

关于Java ConcurrentHashMap 操作原子性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38165869/

相关文章:

performance - 使用 ssd 和 mmap 改进并发文件读取

java - 带索引的列表减法

java - JDialog标题的字体大小

java - 用于绘制可编辑流程图的 Android(或 Android 兼容 Java)库

java - 如何从另一个 AsyncTask 停止(中止)一个 AsyncTask?

java - 当写入未完成时如何检测我正在读取文件?

java - Log4j2 版本 2.3 中多个 JVM 写入同一日志文件

Java 线程不会暂停 I/O 操作

c# - Control.Invoke() 挂起应用程序

java - java中的程序化死锁检测