Java ConcurrentHashMap原子获取(如果存在)

时间:2010-12-04 13:55:26

标签: java concurrenthashmap

如何在并发哈希映射上执行安全获取操作? (与putIfAbsent相同)

错误的例子,不是非常线程安全(检查然后行动情况):

ConcurrentMap<String, SomeObject> concMap = new ...

//... many putIfAbsent and remove operations

public boolean setOption(String id, Object option){
   SomeObject obj = concMap.get(id);

   if (obj != null){
      //what if this key has been removed from the map?
      obj.setOption(option);
      return true;
   }

   // in the meantime a putIfAbsent may have been called on the map and then this
   //setOption call is no longer correct

   return false;
}

另一个不好的例子是:

   public boolean setOption(String id, Object option){
       if (concMap.contains(id)){
           concMap.get(id).setOption(option);
           return true;
       }
       return false;
    }

这里理想的是不要通过同步来添加,删除和获取操作的瓶颈。

由于

4 个答案:

答案 0 :(得分:6)

get()上的ConcurrentHashMap方法是原子的。由于该映射不允许空值,get()实现“get if present”:如果结果为null,则密钥不存在。

答案 1 :(得分:2)

请勿使用containsKey / get,只需致电get即可。如果该方法返回null,则密钥不存在,否则密钥存在,并且您获得了get时映射到的值。

来自文档:

  

返回指定键映射到的值,如果此映射不包含键的映射,则返回null。

这是你的第二个例子的样子:

public boolean setOption(String id, Object option) {

    SomeObject opt = concMap.get(id);
    if (opt == null)
        return false;

    opt.setOption(option);
    return true;
}

答案 2 :(得分:1)

您似乎要做的是将密钥锁定在多个操作上。只有每个操作都是原子的。这些不是锁定密钥的简单方法,只是锁定地图。

然而,在“如果我删除密钥”的情况下,您所能做的就是延迟删除操作,直到调用setOption为止。结果应该是一样的。

您似乎正试图解决可能不需要解决的问题。您还没有解释为什么在删除密钥后或者等待删除密钥时调用setOption是错误的。

答案 3 :(得分:0)

如果您需要对ConcurrentMap中的单个键执行多项操作,可以使用锁定条带化技术来减少争用,这是Guava框架的一个示例:

Action

或者,因为Java 8:ConcurrentMap.compute是一个新的原子方法,看看它是如何在一个键上完成的:

   private Striped<Lock> lock;
    public boolean setOption(String id, Object option) {
      try {
        Lock lock = concMap.get(id);
        lock.lock();
          if (concMap.contains(id)){
          concMap.get(id).setOption(option);
       return true;
   }
   return false;
        } finally {
         lock.unlock();
        }
    }

P.S。可能的变化是ConcurrentMap.computeIfPresent()等。