ConcurrentHashMap put vs putIfAbsent

Dar*_*der 18 java concurrency caching hashmap

Java Docs说,putIfAbsent相当于

   if (!map.containsKey(key)) 
      return map.put(key, value);
   else
      return map.get(key);
Run Code Online (Sandbox Code Playgroud)

因此,如果密钥存在于地图中,则不会更新其值.它是否正确?

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

这是否是添加和更新缓存的更好的方法?

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);
}
Run Code Online (Sandbox Code Playgroud)

Joh*_*int 13

所以它不会更新密钥的价值.它是否正确?

那是正确的.它将返回Map中已有的当前值.

这是添加和更新缓存的更好的方法吗?

一些事情会使您的实施更好.

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

    V local = _cache.get(key);
    if (local.equals(value) && !local.IsExpired()) {
        return;
    }
Run Code Online (Sandbox Code Playgroud)

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

你能做的就是替换

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)


jta*_*orn 5

如果密钥先前不在地图中,则您的代码将引发NPE。

除此之外,尽管这是一个合理的想法,但它不能在“ 并发 ”环境中工作。putIfAbsent()添加该方法的原因是,该映射可以使用其用于使操作具有线程安全性的任何基础支持来管理操作的原子性。在您的实现中,两个不同的调用者可能会彼此终止(第一个调用者用一个新值替换了过期的值,第二个调用者立即用第二个新值替换了第一个新值)。