Java ConcurrentHashMap atomic get if present

久未见 提交于 2019-12-23 13:02:22

问题


How do you perform a safe get if present operation on a Concurrent Hash Map? (same thing like putIfAbsent)

Bad example, not very thread safe (check then act situation):

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

Another bad example would be:

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

The desirable thing here is to not bottleneck the add, remove and get operations by synchronizing them.

Thanks


回答1:


What you appear to be trying to do is to lock a key over multiple operations. Only each operation is atomic. These is no simple way to lock a key, only to lock the map.

However in the "what if I delete a key" case, all you can do is to delay the delete operation until after the setOption is called. The outcome should be the same.

You appear to be trying to solve a problem which may not need to be solved. You haven't explained why calling setOption after a key is deleted or while a the key is waiting to be deleted is bad.




回答2:


The get() method on a ConcurrentHashMap is atomic. Since that map does not allow null values, get() implements "get if present": If the result is null, the key was not present.




回答3:


Don't use containsKey / get, just call get. If that method returns null then the key was not present otherwise the key was present, and you got hold of the value that it was mapped to at the time of the get.

From the docs:

Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.

This is how your second example should look:

public boolean setOption(String id, Object option) {

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

    opt.setOption(option);
    return true;
}



回答4:


If you need to do multiple operations on a single key in ConcurrentMap, you can use either Lock striping technique to reduce contention, here's an example with Guava framework:

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

Or, since Java 8: ConcurrentMap.compute is a new atomic method, see how it is done on a key:

    concMap.compute(keyId, (key, value) -> {
    dosmth; ... return key;  });

p.s. Possible variations are with ConcurrentMap.computeIfPresent(), etc.



来源:https://stackoverflow.com/questions/4353835/java-concurrenthashmap-atomic-get-if-present

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!