【问题标题】:Java CHM synchronisationJava CHM 同步
【发布时间】:2017-01-13 01:25:22
【问题描述】:

跟进这个问题(Java thread safety - multiple atomic operations?),我不想再添加更多问题,但现在我有这个疑问:

private final Map<String, Set<String>> data = Maps.newConcurrentMap();

... then in a method ...

if (data.containsKey("A")) {
    data.get("A").add("B");
}

应该是这样的:

synchronized(data) {
    if (data.containsKey("A")) {
        data.get("A").add("B");
    }
}

为了线程安全。对吗?

所以操作是原子的,但是将它们组合起来需要同步,对吗?到那时,我们手动处理同步时,只使用简单的 HashMap 而不是并发的是否有意义?

在 CHM 中是否有任何方法可以使这项工作原子化?

【问题讨论】:

  • 相对于简单 HashMap 的一个优势是 CHM 仍然不需要读取器同步。
  • 请同时显示修改 CHM 的代码。就 Map 而言,上述代码是只读的(仅调用 get 和 containsKey)。
  • computeIfPresent 不是为此而生的吗?
  • 请问您使用的是哪种线程安全的集合?

标签: java multithreading concurrenthashmap


【解决方案1】:

在您的具体情况下,您可能希望使用computeIfPresent method of ConcurrentHashMap:

data.computeIfPresent("A", (k, v) -> { v.add("B"); return v; } );

来自 javadocs:

如果指定键的值存在,则尝试计算给定键及其当前映射值的新映射。 整个方法调用以原子方式执行。

所以不需要显式同步。

【讨论】:

  • 我担心computeIfPresent() 的(错误)使用来改变值而不是替换它。我不确定在这种情况下原子性保证是否仍然适用。
  • @FedericoPeraltaSchaffner 为什么这样更好?如果元素不存在我不想做任何事情,我只想在元素存在时做。 @shmosel 为什么你说最好做computeIfAbsent?
  • @shmosel:原子性保证仍然有效,但它只扩展到 Map。 Set 也需要是线程安全的(无论如何)。
  • @Will:如果您从多个线程添加,您可能需要putIfAbsent。否则,您可能会覆盖现有(和填充的)集合。
  • 大家非常感谢你们,我学到了很多东西。我认为这是正确的答案,所以我会接受。但是非常感谢@Thilo 的大力帮助,我真的很感激! @shmosel 也是如此!
【解决方案2】:
synchronised(data) {
    if (data.containsKey("A")) {
        data.get("A").add("B");
    }
}

您可能需要显示更多代码。

只看这个,唯一可能的问题是有人在您的if 检查之后删除了在"A" 找到的集合。 如果您从不删除地图条目,则根本不需要同步。

如果您确实同时删除了地图条目,您可以使用computeIfPresent 来获得更新后的地图。

你也可以这样做

Set<String> set = data.get("A");
if (set != null) set.add("B");

由于您实际上并没有产生新的 Set,我发现这比 computeIfPresent 更惯用(它应该计算一个新值)。

请注意,您还需要使所有这些 Set 线程安全。

【讨论】:

  • containsKey() 和 get() 仍然是冗余操作。我会做Set&lt;String&gt; set = data.get("A"); if (set != null) set.add("B");...打败我:)
  • @shmosel:我也是。似乎也比computeIfPresent 好(因为没有计算新值,但现有值发生了变异)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-01
  • 2011-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多