我阅读了所有关于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可以以不同的方式完成吗?
你的答案并不是完全线程安全的。它替换了键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行之间。