Java 同步对ConcurrentMap中给定密钥的访问
我经常想访问(并可能添加/删除)给定Java 同步对ConcurrentMap中给定密钥的访问,java,guava,Java,Guava,我经常想访问(并可能添加/删除)给定ConcurrentMap的元素,以便一次只有一个线程可以访问任何单个键。最好的方法是什么?对密钥本身进行同步不起作用:其他线程可能通过equal实例访问同一密钥 如果答案只适用于由MapMaker构建的映射,那就足够了。您不能创建自己的类来扩展concurrentmap吗。 重写get(objectkey)方法,以便检查请求的key对象是否已被另一个线程“签出” 您还必须在concurrentmap中创建一个新方法,将项目“返回”到映射,以便其他线程可以再次
ConcurrentMap
的元素,以便一次只有一个线程可以访问任何单个键。最好的方法是什么?对密钥本身进行同步不起作用:其他线程可能通过equal
实例访问同一密钥
如果答案只适用于由
MapMaker
构建的映射,那就足够了。您不能创建自己的类来扩展concurrentmap吗。
重写get(objectkey)
方法,以便检查请求的key对象是否已被另一个线程“签出”
您还必须在concurrentmap中创建一个新方法,将项目“返回”到映射,以便其他线程可以再次使用这些项目。请参阅此处的简单解决方案 编辑:此解决方案具有从解锁到锁定的“先发生后清除”关系。然而,下一个解决方案(现已撤回)却没有。
ConcurrentMap
javadoc太轻,无法保证这一点
(撤回)如果要将地图重新用作锁池
private final V LOCK = ...; // a fake value
// if a key is mapped to LOCK, that means the key is locked
ConcurrentMap<K,V> map = ...;
V lock(key)
V value;
while( (value=map.putIfAbsent(key, LOCK))==LOCK )
// another thread locked it before me
wait();
// now putIfAbsent() returns a real value, or null
// and I just sucessfully put LOCK in it
// I am now the lock owner of this key
return value; // for caller to work on
// only the lock owner of the key should call this method
unlock(key, value)
// I put a LOCK on the key to stall others
// now I just need to swap it back with the real value
if(value!=null)
map.put(key, value);
else // map doesn't accept null value
map.remove(key)
notifyAll();
test()
V value = lock(key);
// work on value
// unlock.
// we have a chance to specify a new value here for the next worker
newValue = ...; // null if we want to remove the key from map
unlock(key, newValue); // in finally{}
private final V LOCK=…;//假价值
//如果钥匙映射到LOCK,则表示钥匙已锁定
ConcurrentMap=。。。;
V锁(钥匙)
V值;
while((value=map.putIfAbsent(key,LOCK))==LOCK)
//另一根线在我面前锁住了它
等待();
//现在,putIfAbsent()返回一个实值或null
//我只是成功地锁上了它
//我现在是这把钥匙的锁的主人
返回值;//供来电者处理
//只有密钥的锁所有者才能调用此方法
解锁(钥匙、值)
//我在钥匙上加了一把锁,让别人停下来
//现在我只需要用实际值来交换它
if(值!=null)
map.put(键、值);
else//map不接受空值
映射。删除(键)
notifyAll();
测试()
V值=锁(钥匙);
//创造价值
//解锁。
//我们有机会在这里为下一个工人指定一个新值
newValue=…;//如果要从映射中删除密钥,则为null
解锁(键,新值);//最后{}
这是相当混乱的,因为我们重用地图有两个不同的目的。最好将锁池作为一个单独的数据结构,将map简单地保留为k-v存储。private static final Set lockedKeys=new HashSet();
private static final Set<String> lockedKeys = new HashSet<>();
private void lock(String key) throws InterruptedException {
synchronized (lockedKeys) {
while (!lockedKeys.add(key)) {
lockedKeys.wait();
}
}
}
private void unlock(String key) {
synchronized (lockedKeys) {
lockedKeys.remove(key);
lockedKeys.notifyAll();
}
}
public void doSynchronouslyOnlyForEqualKeys(String key) throws InterruptedException {
try {
lock(key);
//Put your code here.
//For different keys it is executed in parallel.
//For equal keys it is executed synchronously.
} finally {
unlock(key);
}
}
私有无效锁(字符串键)引发InterruptedException{
已同步(锁定键){
而(!lockedKeys.add(key)){
lockedKeys.wait();
}
}
}
私有无效解锁(字符串密钥){
已同步(锁定键){
锁定钥匙。取下(钥匙);
lockedKeys.notifyAll();
}
}
public void dosynchronouslyforeQualkeys(字符串键)抛出中断异常{
试一试{
锁(钥匙);
//把你的代码放在这里。
//对于不同的键,它是并行执行的。
//对于相等的键,它是同步执行的。
}最后{
解锁(钥匙);
}
}
- 键不仅可以是“字符串”,还可以是具有正确重写的“equals”和“hashCode”方法的任何类
- 最后重试-非常重要-您必须保证在操作后解锁等待的线程,即使操作引发异常
- 如果您的后端分布在多个服务器/JVM上,那么它将不起作用
getKey(key)
。你是说你想在查找过程中(对我来说没有太大意义)或在从map获取值之后,在ConcurrentMap
中同步访问吗?@JustYo如果你有一个相等的key实例,为什么您需要地图中的实例?如果该键是可变的,我会怀疑在这里使用该键的设计选择。@JustYo那么为什么要在该键上同步呢?我能想到两种情况:1。阻止对值的并发访问->对值进行同步。2.该值可能会被更改/替换,从而导致类似“脏读”的情况->添加一个间接级别(例如值持有者)并在该级别上进行同步。@如果要删除该值而不是替换该值,则Thomas选项2将不起作用。当然,也可以向值持有者添加一个“空”状态……我更喜欢sjr对链接问题的回答,而不是被接受的答案。为什么?当然,值必须是弱的,因此如果现在没有人需要锁,条目将从锁映射中逐出。映射中仍然有key->weakReference条目“垃圾收集器可能会回收映射中存在的密钥或值。如果发生这种情况,条目将自动从映射中消失。”如果我不再有对键的引用,我肯定不会有对值的引用(只能通过映射访问),相应的条目将被垃圾收集。我明白了。我对MapMaker不熟悉。但如果锁对象可以消失,然后重新创建,不同的线程可能会在不同的对象上同步,并且在关系可能不成立之前发生