繁体   English   中英

java并发在map值上同步

[英]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.
});

请注意,前面示例中的mapConcurrentHashMap一个实例。 这将确保值处理一次( 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM