Java ConcurrentHashMap操作原子性



这可能是一个重复的问题,但我在一本关于并发的书中发现了这部分代码。据说这是线程安全的:

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)为您提供原子性。来自文档(强调我的(:

如果当前映射到给定值,则仅替换键的条目。。。该动作是以原子方式执行的。

关键词是"原子性的"。在replace中,"检查旧值是否是我们所期望的,只有当它是时,才替换它"作为一个单独的工作块发生,没有其他线程能够与它交织。这取决于实现是否进行所需的任何同步,以确保它提供这种原子性。

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

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

有人能解释一下代码是否正常吗?

除了yshavit的答案之外,您还可以通过使用Java8中添加的compute来避免编写自己的循环。

ConcurrentMap<String, Integer> counts = new ...;
private void countThing(String thing) {
    counts.compute(thing, (k, prev) -> prev == null ? 1 : 1 + prev);
}

使用put也可以替换值。

if (currentCount == null) {
        counts.put(thing, 2);
    }

相关内容

  • 没有找到相关文章

最新更新