List<String> list = Collections.synchronizedList(new ArrayList<String>());
synchronized (list) {
list.add("message");
}
块“synchronized(list){}”真的需要吗?
答案 0 :(得分:93)
您不需要像放入示例中那样进行同步。但是,非常重要的是,在迭代时需要在列表周围进行同步(如Javadoc中所述):
用户必须手动同步返回的内容 迭代时列出:
List list = Collections.synchronizedList(new ArrayList()); ... synchronized(list) { Iterator i = list.iterator(); // Must be in synchronized block while (i.hasNext()) foo(i.next()); }
答案 1 :(得分:29)
这取决于synchronized
块的确切内容:
如果块在列表上执行单个原子操作(如您的示例所示),则synchronized
是多余的。
如果块在列表上执行多项操作 - 并且需要在复合操作的持续时间内保持锁定 - 那么synchronized
是不是多余的。一个常见的例子是迭代列表。
答案 2 :(得分:20)
Collections.synchronizedList add方法的基础代码是:
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);}
}
因此,在您的示例中,不需要添加同步。
答案 3 :(得分:16)
另外需要注意的是,任何使用Iterators的方法(例如Collections.sort())也需要封装在synchronized块中。
答案 4 :(得分:7)
阅读此Oracle Doc
它说“当迭代时,用户必须手动同步返回的列表”
答案 5 :(得分:1)
与其他人提到的一样,同步集合是线程安全的,但默认情况下,对这些集合的复合操作不保证是线程安全的。
根据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) - &gt;除去
线程B(getLast)--------------------&gt;得到
线程A remove
线程B中get
操作之前的元素。因此,线程B仍然使用10作为lastIndex
来调用list.get
方法,它会导致并发问题。