【问题标题】:ConcurrentHashMap and operations that span 2 separate callsConcurrentHashMap 和跨越 2 个单独调用的操作
【发布时间】:2019-05-06 22:56:35
【问题描述】:

我知道 ConcurrentHashMap 方法是线程安全的,但是对于非原子的复合操作,例如查看这段代码 - 如果 2 个不同的线程同时使用相同的“myKey”调用它,那是不可能的竞争条件发生?

myMap 是一个 ConcurrentHashMap

myValues = myMap.get(myKey);
if (myValues == null) {
    myValues = new List()
    myMap.add(myKey, myValues);
}
myValue.add(new list item);

如何以线程安全的方式编写上述内容,仅使用 ConcurrentHashMap 和不使用单独的锁等,

putIfAbsent 似乎也没有解决问题,或者类似这样可行:

myValues =  myMap.putIfAbsent(myKey, new List());
myValues.add(new list item);

这是否正确且线程安全?
谢谢

【问题讨论】:

  • 无论您如何处理ConcurrentHashMap,您都试图同时从多个线程追加到同一个列表。仅仅因为列表恰好是ConcurrentHashMap 中的一个值,这并不安全。

标签: java concurrency concurrenthashmap


【解决方案1】:

使用computeIfAbsent,它是原子的:

文档:

如果指定的键尚未与值关联,则尝试 使用给定的映射函数计算其值并输入 除非为空,否则进入此地图。执行整个方法调用 原子地,因此每个键最多应用该函数一次。一些 其他线程在此地图上尝试的更新操作可能是 在计算过程中被阻塞,所以计算应该是 简短而简单,并且不得尝试更新任何其他映射 这张地图。

简单用法:

ConcurrentHashMap<String, List<String>> map = new ConcurrentHashMap<>();
List<String> list = map.computeIfAbsent("myKey", key -> new CopyOnWriteArrayList<>());

编辑
正如@Holger 正确注意到的那样,ArrayList 在这种情况下使用不安全。使用CopyOnWriteArrayList 更安全,它是ArrayList 的线程安全变体。

【讨论】:

  • @Holger,你所说的“破碎”是什么意思?
  • ArrayList 不是线程安全的。所以它不能被多个线程同时使用。但是在ConcurrentHashMap 上使用computeIfAbsent("myKey", key -&gt; new ArrayList&lt;&gt;()) 的全部目的是为了让多个线程使用它,而不需要额外的同步。 OP 的示例已经包括在其上调用 add
  • @Holger,你当然是对的。我只是想展示如何创建和使用ArrayList 的新列表,例如。但是这个粗心的例子给列表本身带来了问题。感谢您的评论!
猜你喜欢
  • 2016-12-08
  • 1970-01-01
  • 1970-01-01
  • 2018-10-05
  • 1970-01-01
  • 2019-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多