提问者:小点点

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


我阅读了所有关于JavaConCurrentHashMap用法的相反之处。我希望我的问题能帮助澄清一些看起来简单的东西(有最新的答案)。

我有一张这样的地图:

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

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

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

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

此代码对并发读/写安全吗?使用JavaAPI可以以不同的方式完成吗?


共2个答案

匿名用户

你的答案并不是完全线程安全的。它替换了键id的值,而不管旧值是什么。如果这对你的实现来说没问题,那么很好,但是替换(K key,V oldObj,V newObj)是替换并发映射中现有值的理想检查和设置(CAS)方式。

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

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

这确保了仅当前一个值是oldObj时才更新映射。如果此代码块被具有不同newValue的不同线程调用会发生什么?。上述代码只会让一个线程成功,而另一个线程将返回false。

匿名用户

这是回答我自己的问题的尝试。它极大地启发了我发现的另一个问题答案:在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);

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