ConcurrentHashMap put vs putIfAbsent

时间:2012-05-07 17:31:06

标签: java caching concurrency hashmap

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);
}

2 个答案:

答案 0 :(得分:12)

  

所以它不会更新密钥的值。这是对的吗?

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

  

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

有些事情可以让您的实施更好。

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

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

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

您可以做的是replace

完成所有操作后,它可能看起来像这样

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;
    }
}

答案 1 :(得分:5)

如果密钥以前不在地图中,您的代码将抛出NPE。

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