ConcurrentHashMap put vs putIfAbsent

2022-09-02 10:40:12

Java Docs说,相当于putIfAbsent

   if (!map.containsKey(key)) 
      return map.put(key, value);
   else
      return map.get(key);

因此,如果映射中存在该键,则不会更新其值。这是正确的吗?

如果我想根据某些条件更新密钥值怎么办?比如说到期时间等。

这是否是添加和更新缓存的更好含义?

public void AddToCache(T key, V value)
{
   V local = _cache.putifabsent(key, value);

   if(local.equals(value) && local.IsExpired() == false){
     return;
   }
   // this is for updating the cache with a new value
   _cache.put(key, value);
}

答案 1

因此,它不会更新键的值。这是正确的吗?

这是正确的。它将返回地图中已有的当前值。

这对于添加和更新缓存会更好吗?

有几件事会让你的实现更好。

1.您不应该使用putIfAbsent来测试它是否存在,您应该只在想要确保如果不存在那么时才使用它。相反,您应该使用来测试它的存在(或map.contains)。putIfAbsentmap.get

    V local = _cache.get(key);
    if (local.equals(value) && !local.IsExpired()) {
        return;
    }

2. 不是 put,而是您想要替换,这是因为可能发生争用条件,其中两个或多个线程可以评估为 false,其中两个(或更多)线程中的一个将覆盖另一个线程的 put。if

相反,您可以做的是替换

当一切都说完了,做完了,它可能看起来像这样

public void AddToCache(T key, V value) {
    for (;;) {

        V local = _cache.get(key);
        if(local == null){
            local = _cache.putIfAbsent(key, value);
            if(local == null)
                return;
        }
        if (local.equals(value) && !local.IsExpired()) {
            return;
        }

        if (_cache.replace(key, local, value))
            return;
    }
}

答案 2

如果密钥以前不在映射中,则代码将引发 NPE。

除此之外,尽管这是一个合理的想法,但它在“并发”环境中不起作用。添加该方法的原因是,映射可以使用它使用的任何底层支持来管理操作的原子性,以使操作线程安全。在您的实现中,2 个不同的调用方可能会结束相互单步执行(第一个调用方将过期的值替换为新值,第二个调用方立即将第一个新值替换为第二个新值)。putIfAbsent()