[英]ConcurrentHashMap putIfAbsent : atomicity when followed by a get() call
我想讨论一个特定的用途,我有一个并发映射来感知检查我的逻辑......
如果我使用ConcurrentHashMap
,我可以做熟悉的
private final ConcurrentHashMap<K, V> map = new ConcurrentHashMap<K, V>();
public V getExampleOne(K key) {
map.putIfAbsent(key, new Object());
return map.get(key);
}
但我意识到存在竞争条件 ,如果我从putIfAbsent
和get
之间的地图中删除项目,上面的方法将返回集合中不再存在的东西。 这可能会或可能不会很好,但我们假设对于我的用例,它不行。
我真正喜欢的是让整个事物成为原子。 所以,
public V getExampleTwo(K key) {
return map.putIfAbsent(key, new Object());
}
但随着这扩大到
if (!map.containsKey(key))
return map.put(key, value); [1]
return map.get(key);
对于第一次使用,第[1]行将返回null
(即, map.put
将返回先前的值,第一次使用时为null
)。
我不能让它在这个实例中返回null
这让我有类似的东西;
public V getExampleThree(K key) {
Object object = new Object();
V value = locks.putIfAbsent(key, object);
if (value == null)
return object;
return value;
}
最后,我的问题; 上面的例子在语义上有何不同? getExampleThree
是否确保像getExampleTwo
这样的原子性但是正确地避免了null返回? getExampleThree
还有其他问题吗?
我希望对这些选择进行一些讨论。 我意识到我可以使用非ConcurrentHashMap
并同步调用我的get
方法的客户端和从地图中删除的方法,但这似乎打败了ConcurrentHashMap的目的(非阻塞性质)。 这是我保持数据准确的唯一选择吗?
我想这是你选择ConcurrentHashMap的原因之一; 它与您交互时的可见/最新/显示,但如果旧数据将成为一个问题,可能会对此产生影响......
听起来您正在尝试为密钥创建全局锁定对象。
我不会删除一个可能几乎立即重新创建的条目,而是当你非常确定它不再需要时,我只会删除该条目。
否则,如果您将其用于锁定,则可以在同一个键的不同对象上使用两个线程锁定。
如果不行,你可以忙着循环它。
public V getExampleOne(K key) {
for(Object o = null, ret = null; (ret = map.get(key)) == null; )
map.putIfAbsent(key, o == null ? o = new Object() : o);
return ret;
}
一旦循环存在,它仍然可以被移除或替换,因此它实际上与它有效相同。
public V getExampleThree(K key) {
Object o = new Object();
map.putIfAbsent(key, o);
Object ret = map.get(key);
return ret == null ? o : ret;
}
最后,我的问题; 上面的例子在语义上有何不同?
差异只是显而易见的。
getExampleThree是否确保像getExampleTwo这样的原子性但是正确地避免了null返回?
是。
getExampleThree还有其他问题吗?
只有当你认为下次调用可能不会给你一个不同的值时(如果你认为它可以在另一个线程中删除)
这些方法有不同的语义:
但是,根据情况,它可能不是您使用返回值时的实际对象。 然后,您需要显式锁定。
为什么不简单地使用第一个版本并同步方法?
public synchronized V getExampleOne(K key) {
map.putIfAbsent(key, new Object());
return map.get(key);
}
虽然它不会为您提供最大的并行性,但您也只有两个操作和getExampleThree
,而且正确,读取代码的其他人不太容易理解。
我想你会发现诀窍是假设你将是非原子的并处理它。
我不太清楚你在寻找什么。 让我知道如果这是切断的,我会修改。
也许你正在寻找类似的东西:
private final ConcurrentHashMap<String, Object> map = new ConcurrentHashMap();
/*
* Guaranteed to return the object replaced.
*
* Note that by the time this method returns another thread
* may have already replaced the object again.
*
* All we can guarantee here is that the return value WAS
* associated with the key in the map at the time the entry was
* replaced.
*
* A null return implies that either a null object was associated
* with the specified key or there was no entry in the map for
* the specified key wih 'null' as it's 'value'.
*/
public Object consistentReplace ( String key, Object newValue ) {
Object oldValue = map.get(key);
while ( !map.replace(key, oldValue, newValue) ) {
// Failed to replace because someone got in there before me.
oldValue = map.get(key);
}
return oldValue;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.