[英]java concurrency synchronized on map value
下面的代码,我很困惑当2个线程竞争map.get(k)
的锁时会发生什么。 当线程A获胜时,它使map.get(k)
null,第二个线程将获得synchronized(null)
? 或者它是否两个线程都将它看作synchronized(v)
即使第一个线程将其更改为null但在哪个线程B仍然将其视为v
?
synchronized(map.get(k)) {
map.get(k).notify();
map.remove(k);
}
问题类似于另一个问题 ,除了锁定对象是地图的值。
更新 :比较这篇文章和上述链接中的讨论,是否属实
synchronized(v) {
v.notify();
v = null;
}
会导致第二个线程synchronized(null)
。 但是对于synchronized(map.get(k))
,第二个线程会synchronized(v)
???
更新 :要回答@ Holger的问题,这篇文章与另一篇文章的主要区别在于:
final V v = new V();
synchonized(map.get(k)) {
map.get(k).notify();
map.remove(k);
}
第二个线程不会“请求”对thread.get(k)
的锁定,两个线程都会在第一个线程开始执行之前请求对map.get(k)
的结果进行锁定。 所以代码大致类似于:
Object val = map.get(k);
val.notify();
因此,当获得锁的线程完成执行时,第二个线程仍然会引用Object val
,即使map[k]
不再指向它(或指向null
)
编辑 :(以下许多有用的评论)
似乎正在获取map.get(k)
上的锁以确保仅执行一次处理map.remove(k)
在处理之后调用map.remove(k)
)。 虽然2个竞争val
上的锁的线程都不会遇到null.notify()
,但是这个代码的安全性并不能保证, 因为第二个线程可能会在第一个线程之后调用synchronized(map.get(k))
一个已经退出同步块 。
为了确保原子地处理k
,可能需要更安全的方法。 一种方法是使用并发哈希映射,如下所示:
map.computeIfPresent(k, (key, value) -> {
//process the value here
//key is k
//value is the value to which k is mapped.
return null; //return null to remove the value after processing.
});
请注意,前面示例中的map
是ConcurrentHashMap
一个实例。 这将确保值处理一次( computeIfPresent
以原子computeIfPresent
运行)。
引用ConcurrentHashMap.computeIfAbsent
文档注释:
如果存在指定键的值,则尝试在给定键及其当前映射值的情况下计算新映射。 整个方法调用以原子方式执行。 其他线程在此映射上的某些尝试更新操作可能在计算进行时被阻止,因此计算应该简短,并且不得尝试更新此映射的任何其他映射。
会发生什么是您将锁定密钥k
的hashmap条目中当前的值。
问题#1 - 如果map.get(k)
调用返回null
,那么你将得到一个NPE。
问题#2 - 因为你没有锁定map
:
map.put(k, v)
,其中v
与您锁定的v
不同,并且 map.remove(k)
可能导致(可能)导致地图数据结构损坏的内存异常。 通过在map.get(k)
(而不是map
)上进行同步,目前尚不清楚你实际想要实现的目标。 但不管它是什么,这段代码都不是线程安全的。
重新更新:是的,这是真的...假设其他线程正在同步变量v
的值。 请注意,您始终在对象上进行同步,因此当您执行synchronized(v)
,这意味着“获取v
的当前值并在该对象上synchronized(v)
”。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.