如何执行并发安全的映射对象操作?

3

我看到了很多有关Java ConcurrentHashMap使用的信息,有些说法相反。我希望我的问题能够帮助澄清一些看似简单(但实际上需要最新答案)的问题。

我有一个像这样的映射:

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

我正在使用ConcurrentHashMap来保证put和get操作的线程安全性。我的ClassA有一些整数/字符串属性和一个字符串集合。
现在我想知道如何安全地更新我的映射对象。如果我想创建一个方法通过添加新的字符串来更新我的map中的对象,我会有类似下面的代码:
        synchronized(map)
        {
            Collection<String> strings = map.get(id).getStrings();
            if(!strings.contains(newString)) //strings can't be null
            {
                strings.add(newString);
            }
        }

这段代码在并发读写时是否安全?是否可以使用Java API进行不同的处理?


你的示例可能有一些错误。现在,同步块内部的代码与 map 没有任何关系。有一个名为 events 的变量,但我不知道那是什么——我猜它是你的 map。如果是这种情况,那么你真的不需要在 map 上进行同步。提取此逻辑,将其推入 ClassA,然后进行一些同步操作。 - bbankowski
确实,我已经使用正确的名称更新了描述。这确实是我的地图。我希望能够将地图同步,以便实际上锁定整个表格并防止对我的映射对象进行任何并发更改。您能详细说明一下“某些同步”吗? - TBag
最简单的方法是在ClassA中创建一个公共的void synchronized add(String newString) 方法。ClassA实例的并发修改将被同步,您不必在map上进行同步。 - bbankowski
如果你通过创建外部同步层使所有操作都安全,则使用ConcurrentHashMap就没有意义了。 - lbalazscs
@lbalazscs 我认为可能还有,因为我们在这里没有看到put/get操作。当然,看整个类会更好,以做出正确的决定。 - bbankowski
感谢大家尝试回答我的问题。我发布了一个可能的答案,它与使用ConcurrentHashMap的方式相同。我很想听听你们的意见! - TBag
2个回答

2

你所提供的答案并不完全是线程安全的。它替换了键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。


如果我想要防止在获取和替换之间出现任何线程竞争,那么我应该在地图本身上进行同步吗? - TBag
如果存在竞争,您不需要这样做。最多只有1次调用replace会成功。第二次replace将返回false。如果您想要两者都成功,您必须将其放入do/while循环中。 - Nagesh Susarla

0
这是我尝试回答自己的问题。它受到我发现的另一个问题答案的启发:什么是修改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行之间获取相同的对象。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接