如何在并发哈希映射上执行安全获取操作? (与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;
}
这里理想的是不要通过同步来添加,删除和获取操作的瓶颈。
由于
答案 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()等。