【发布时间】:2012-03-04 09:51:05
【问题描述】:
我想在“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();
}
}
我不同步数据结构,而是同步所有可能的公共方法。那么哪些会起作用,哪个更好呢?
【问题讨论】:
-
old = new Set<Value>();-Set是一个接口,你用的是什么类?如果使用非并发类可能会出现问题 -
这有关系吗?我正在考虑使用 CopyOnWriteArraySet,它保证线程安全,但映射或访问器的同步是否已经保证一次只有一个线程会查看 Set?
-
为什么第一个实现保证一次只有一个线程会查看
Set?我不认为这种说法是正确的,如果集合不是线程安全的,那么当 2 个线程尝试同时从集合中添加/删除元素时,您可能会遇到并发问题。 -
我想我看到了第一个实现的问题。第二个呢?
标签: java multithreading collections concurrency synchronization