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, SomeO开发者_如何学运维bject> 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; });
p.s. Possible variations are with ConcurrentMap.computeIfPresent(), etc.
精彩评论