为什么我需要同步Collections.synchronizedList返回的列表

我发现这是在dos.oracle.com

public static List synchronizedList(列表列表)

返回由指定列表支持的同步(线程安全)列表。 为了保证串行访问,必须通过返回的列表完成对后备列表的所有访问。 当迭代它时,用户必须手动同步返回的列表:

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();我为什么必须同步列表来迭代它Collections.synchronizedList(); 应该返回一个已经同步的列表?

我只是在两个线程中访问列表:一个线程只添加,另一个线程获取和删除。 您建议在此方案中使用哪些其他类?

谢谢阅读。

正在同步的列表仅意味着addremove等操作是同步的,因此是primefaces的。 然而,迭代不是,如果一个线程在另一个迭代时adds ,你可能会得到一个ConcurrentModificationException。

通过手动同步迭代块,可确保在迭代时不会修改列表。

一种替代方法是使用CopyOnWriteArrayList ,它提供迭代器 ,迭代器在迭代开始时已知的列表上进行迭代,而不管后续修改。 但是,如果您需要经常更改列表的内容,那么该集合效率不高。

起初我对这个主题感到有点困惑,因为我发现的大多数例子都没有上下文。 我终于找到了这篇博文,为我解决了这个问题: http : //netjs.blogspot.de/2015/09/how-and-why-to-synchronize-arraylist-in-java.html

从上面的示例看起来我只需要使用Collections.synchronizeList()转换我的列表然后我可以添加和删除项目而不必担心线程安全。 但是请注意,在将列表传递给不同的线程之前必须同步列表,因为否则列表访问不是互斥的。

所以一个完整的例子是:

 public class SynchroProblem implements Runnable{ private List myList; //Constructor public SynchroProblem(List myList){ this.myList = myList; } @Override public void run() { // Do stuff with the list .add(), .remove(), ... myList.add(5); // Even if mylist is synchronized the iterator is not, // so for using the iterator we need the synchronized block synchronized (myList){ // do stuff with iterator eg Iterator iterator = myList.iterator(); while (iterator.hasNext()){ int number = iterator.next(); if (number == 123){ iterator.remove(); } } } } public static void main(String[] args) { List originalList = new ArrayList(); // Synchronize list List syncList = Collections.synchronizedList(originalList); // Create threads and pass the synchronized list Thread t1 = new Thread(new SynchroProblem(syncList)); Thread t2 = new Thread(new SynchroProblem(syncList)); t1.start(); t2.start(); } }