我们正在编写一些锁定代码,并遇到了一个特殊的问题。我们使用 ConcurrentHashMap 来获取我们锁定的对象实例。所以我们的同步块看起来像这样
synchronized(locks.get(key)) { ... }
我们重写了 ConcurrentHashMap 的 get 方法,使其在不包含键的情况下始终返回一个新对象。
@Override
public Object get(Object key) {
Object o = super.get(key);
if (null == o) {
Object no = new Object();
o = putIfAbsent((K) key, no);
if (null == o) {
o = no;
}
}
return o;
}
但是有没有一种状态,get方法已经返回了对象,但是线程还没有进入synchronized块。允许其他线程获取相同的对象并锁定它。
我们有一个潜在的竞争条件是
- 线程1:获取key为A的对象,但不进入synchronized块
- 线程2:获取带有键A的对象,进入同步块
- 线程2:从映射中删除对象,退出同步块
- 线程1:进入同步块,对象不再在map中
- 线程 3:获取键 A 的新对象(与线程 1 获取的对象不同)
- 线程 3:进入同步块,而线程 1 也在其同步块中,两者都使用键 A
如果 java 在 get 调用返回后直接进入同步块,则不会出现这种情况。如果没有,是否有人对我们如何删除密钥而不必担心这种竞争条件有任何意见?
在我看来,问题源于你锁定了映射值,而实际上您需要锁定key(或者它的一些派生)。如果我理解正确的话,您希望避免 2 个线程使用相同的键运行关键部分。
你可以锁定钥匙吗?你能保证你总是使用同一个密钥实例吗?
一个不错的选择:
根本不要删除锁。用一个参考图 http://commons.apache.org/collections/apidocs/org/apache/commons/collections/map/ReferenceMap.html价值观较弱。这样,只有当当前没有任何线程使用映射条目时,才会删除该条目。
Note:
1) 现在您必须同步此映射(使用 Collections.synchronizedMap(..))。
2)您还需要同步为给定键生成/返回值的代码。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)