简体   繁体   中英

Java ConcurrentHashMap atomic get if present

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

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.

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

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.

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

ps Possible variations are with ConcurrentMap.computeIfPresent(), etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM