【发布时间】:2012-03-17 02:42:42
【问题描述】:
List<String> list = Collections.synchronizedList(new ArrayList<String>());
synchronized (list) {
list.add("message");
}
这里真的需要块“同步(列表){}”吗?
【问题讨论】:
标签: java collections synchronization
List<String> list = Collections.synchronizedList(new ArrayList<String>());
synchronized (list) {
list.add("message");
}
这里真的需要块“同步(列表){}”吗?
【问题讨论】:
标签: java collections synchronization
您不需要像您在示例中那样进行同步。但是,非常重要的是,您需要在迭代时围绕列表进行同步(如 Javadoc 中所述):
用户必须手动同步返回的 迭代时列出:
List list = Collections.synchronizedList(new ArrayList()); ... synchronized(list) { Iterator i = list.iterator(); // Must be in synchronized block while (i.hasNext()) foo(i.next()); }
【讨论】:
这取决于synchronized 块的确切内容:
如果块在列表上执行单个原子操作(如您的示例中所示),则 synchronized 是多余的。
如果块在列表上执行多个操作 -- 并且需要在复合操作期间保持锁定 -- 那么synchronized是不 多余的。一个常见的例子是遍历列表。
【讨论】:
Collections.synchronizedList add 方法的底层代码是:
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);}
}
因此,在您的示例中,不需要添加同步。
【讨论】:
还需要注意的是,任何使用迭代器的方法(例如 Collections.sort())也需要封装在同步块中。
【讨论】:
阅读此Oracle Doc
上面写着“用户在迭代返回的列表时必须手动同步它”
【讨论】:
就像其他人提到的那样,同步集合是线程安全的,但默认情况下,这些集合的复合操作并不保证是线程安全的。
根据JCIP,常见的复合动作可以是
OP的同步代码块不是复合动作,加不加没区别。
让我们以 JCIP 中的示例为例,对其进行一些修改,以阐明为什么需要用锁来保护复合动作。
有两种方法对list 包裹的同一个集合Collections.synchronizedList 进行操作
public Object getLast(List<String> list){
int lastIndex = list.size() - 1;
return list.get(lastIndex);
}
public void deleteLast(List<String> list){
int lastIndex = list.size() - 1;
list.remove(lastIndex);
}
如果方法getLast 和deleteLast 被两个不同的线程同时调用,下面的交错可能发生,getLast 将抛出ArrayIndexOutOfBoundsException。假设当前lastIndex 为10。
线程 A (deleteLast) --> 删除
线程 B (getLast) --------------------> 获取
线程A的remove是线程B中get操作之前的元素。因此,线程B仍然使用10作为lastIndex调用list.get方法,会导致并发问题。
【讨论】: