【问题标题】:How to perform concurrency safe mapped object operations?如何执行并发安全的映射对象操作?
【发布时间】:2015-01-19 22:01:44
【问题描述】:

我阅读了有关 Java ConcurrentHashMap 用法的所有内容及其相反的内容。我希望我的问题将有助于澄清一些看起来很简单的事情(提供最新的答案)。

我有一张这样的地图:

ConcurrentHashMap<Integer, ClassA> map = new ConcurrentHashMap<Integer, ClassA>()

我正在使用 ConcurrentHashMap 来保持放置和获取操作线程的安全。我的 ClassA 有一些整数/字符串属性和字符串集合。

现在我想知道如何安全地更新我的映射对象。如果我想创建一个方法来通过添加一个新字符串来更新我的地图中的对象,我有类似的东西:

        synchronized(map)
        {
            Collection<String> strings = map.get(id).getStrings();
            if(!strings.contains(newString)) //strings can't be null
            {
                strings.add(newString);
            }
        }

这段代码对并发读/写安全吗?可以通过使用 Java API 以不同的方式完成吗?

【问题讨论】:

  • 您的示例可能有一些错误。现在 synchronized 块内的代码与 map 无关。有一个名为 events 的变量,但我不知道那是什么 - 我猜是你的地图。如果是这样的话,那么你真的不需要在地图上同步。提取此逻辑,将其推送到 ClassA 中,然后进行一些同步。
  • 确实,我用正确的名称更新了描述。这确实是我的地图。我希望同步地图以实际锁定整个表并防止对我的映射对象进行任何并发更改。你能详细说明“一些同步”吗?
  • 最简单的方法是在 ClassA 中创建 public void synchronized add(String newString) 方法。 ClassA 实例的并发修改将被同步,您不必在地图上同步。
  • @bbankowski 如果您通过创建外部同步层来确保所有操作的安全,那么使用 ConcurrentHashMap 是没有意义的
  • @lbalazscs 我认为仍然可能存在,因为我们在这里看不到 put/get 操作。当然,最好能看到全班同学,做出正确的决定。

标签: java multithreading concurrency concurrenthashmap


【解决方案1】:

您的答案不是完全线程安全的。无论旧值是什么,它都会替换键 id 的值。如果这对您的实现来说没问题,那么很好,但replace(K key, V oldObj, V newObj) 是替换 ConcurrentMap 中现有值的理想检查和设置 (CAS) 方式。

在您的特定用例中,您将执行以下操作

ClassA oldObj = map.get(id);
ClassA newObject = new ClassA(oldObject, newValue);
return map.replace(id, oldObj, newObject);

这可确保仅当先前的值为 oldObj 时才更新地图。如果此代码块由具有不同newValue 的不同线程调用会发生什么?上面的代码只会让一个线程成功,另一个线程会返回false。

【讨论】:

  • 如果我想防止 get 和 replace 之间的任何线程竞争,我应该在地图本身上同步吗?
  • 你不需要。如果有比赛,最多 1 次调用 replace 会成功。第二次替换将返回 false。如果您希望两者都成功,则必须将其放入 do/while 循环中
【解决方案2】:

这是试图回答我自己的问题。这对我发现的另一个问题的答案很有启发:What is the preferred way to modify a value in ConcurrentHashMap?

如果我让我的ClassA 不可变,并用这种方式替换我的代码以更新我的地图对象:

        ClassA oldObject = map.get(id);
        ClassA newObject = new ClassA(oldObject, newValue);//this constructor copies the old one and add the newValue in my Collection of Strings
        map.put(id, newEvent);

它是线程安全的吗?我真的很想通过使用ConcurrentHashMap 来保持我的代码干净和高效,我相信这个解决方案正朝着同一个方向发展。 我只是有点怀疑任何线程检索相同的对象为什么另一个会在第 2 行和第 3 行之间。

【讨论】:

    猜你喜欢
    • 2016-07-10
    • 2022-01-10
    • 2019-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多