组合putIfAbsent的原子性和用ConcurrentMap替换

时间:2014-09-07 03:10:56

标签: java multithreading concurrency java.util.concurrent

这是指对in this thread

提出的问题的公认解决方案
public void insertOrReplace(String key, String value) {
    for (;;) {
        String oldValue = concurrentMap.putIfAbsent(key, value);
        if (oldValue == null)
            return;

        final String newValue = recalculateNewValue(oldValue, value);
        if (concurrentMap.replace(key, oldValue, newValue))
            return;
    }
}

我很想彻底了解代码。

我相信putIfAbsentreplace可以被视为复合操作。对象oldValue没有显式同步,这个复合操作是原子的吗?或者仅仅因为for循环,它保证了原子性?循环到底是做什么的?它会导致无限循环使方法永远不会结束吗?

1 个答案:

答案 0 :(得分:0)

首先,原始问题引入了一些奇怪的代码。不确定它有什么好处,但让我们把它放在一边。

第二件事,所选答案和你在这里复制粘贴的答案只是与问题中代码相同的不同方式。

那么,这个(奇异)方法做了什么:

  1. 检查密钥是否映射到某个值,如果没有,则分配该值并存在。
  2. 如果是,请重新计算该值并替换旧值。
  3. 为什么我们需要循环呢?如果2个线程正在重新计算该值并进行“一起”替换,则只有1个将成功,因此从该方法返回。 “松动”线程需要再次通过该过程。可能会在putIfAbsent调用中获得一个新值,并重新计算要替换的新值,这次可能没问题。

    有几点需要注意:

    1. 我建议您仔细阅读ConcurrentMap的{​​{1}}和putIfAbsent方法的API。
    2. 有一个极端情况(实际上永远不会发生),对此方法的调用将永远停留,因为它总是会“松散”到另一个调用。
    3. 我怀疑同步这整个方法会更好地避免这种繁琐的循环。但由于我不完全清楚这段代码旨在解决的问题,我无法正式宣称这一点。