我想在“Map of Sets”集合上实现一个变体,它将被多个线程不断访问。我想知道我正在进行的同步是否足以保证不会出现任何问题。
所以给出如下代码,其中Map、HashMap和Set是Java实现,Key和Value是一些任意对象:
public class MapOfSets {
private Map<Key, Set<Value>> map;
public MapOfLists() {
map = Collections.synchronizedMap(new HashMap<Key, Set<Value>());
}
//adds value to the set mapped to key
public void add(Key key, Value value) {
Set<Value> old = map.get(key);
//if no previous set exists on this key, create it and add value to it
if(old == null) {
old = new Set<Value>();
old.add(value);
map.put(old);
}
//otherwise simply insert the value to the existing set
else {
old.add(value);
}
}
//similar to add
public void remove(Key key, Value value) {...}
//perform some operation on all elements in the set mapped to key
public void foo(Key key) {
Set<Value> set = map.get(key);
for(Value v : set)
v.bar();
}
}
这里的想法是,因为我已经同步了 Map 本身,所以 get() 和 put() 方法应该是原子的,对吧?因此应该不需要对 Map 或其中包含的 Sets 进行额外的同步。那么这行得通吗?
或者,上述代码是否优于另一种可能的同步解决方案:
public class MapOfSets {
private Map<Key, Set<Value>> map;
public MapOfLists() {
map = new HashMap<Key, Set<Value>();
}
public synchronized void add(Key key, Value value) {
Set<Value> old = map.get(key);
//if no previous set exists on this key, create it and add value to it
if(old == null) {
old = new Set<Value>();
old.add(value);
map.put(old);
}
//otherwise simply insert the value to the existing set
else {
old.add(value);
}
}
//similar to add
public synchronized void remove(Key key, Value value) {...}
//perform some operation on all elements in the set mapped to key
public synchronized void foo(Key key) {
Set<Value> set = map.get(key);
for(Value v : set)
v.bar();
}
}
我让数据结构不同步,而是同步所有可能的公共(public)方法。那么哪些可行,哪些更好?
最佳答案
您发布的第一个实现不是线程安全的。考虑一下当两个具有相同 key
的并发线程访问 add
方法时会发生什么:
- 线程 A 执行该方法的第 1 行,并获得一个
null
引用,因为不存在具有给定键的项目 - thread B 执行该方法的第 1 行,并获得一个
null
引用,因为不存在具有给定键的项目 — 这将在 A< 之后发生/strong> 从第一次调用返回,因为 map 是同步的 - 线程 A 评估 if 条件为
false
- 线程 B 评估 if 条件为
false
从那时起,两个线程将继续执行 if 语句的 true
分支,您将丢失两个 value
对象之一。
您发布的方法的第二种变体看起来更安全。
但是,如果你可以使用第三方库,我建议你查看Google Guava ,因为它们提供并发多图 ( docs )。
关于java - 同步集合/列表的映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9553888/