List<String> list = Collections.synchronizedList(new ArrayList<String>());
synchronized (list) {
list.add("message");
}
这里真的需要块“ synchronized(list){}”吗?
Answers:
在示例中,您无需同步。但是,非常重要,在迭代列表时需要在列表周围进行同步(如Javadoc中所述):
当用户遍历返回列表时,必须手动对其进行同步:
List list = Collections.synchronizedList(new ArrayList()); ... synchronized(list) { Iterator i = list.iterator(); // Must be in synchronized block while (i.hasNext()) foo(i.next()); }
Collections.synchronizedList add方法的基础代码是:
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);}
}
因此,在您的示例中,不需要添加同步。
同样重要的是要注意,任何使用Iterators的方法(例如Collections.sort())也都需要封装在一个同步块中。
就像其他人提到的一样,同步的集合是线程安全的,但是默认情况下,不能保证对这些集合的复合操作是线程安全的。
根据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在线程B中remove
进行get
操作之前的元素。因此,线程B仍使用10作为lastIndex
to调用list.get
方法,这将导致并发问题。