我在dos.oracle.com找到了这个
public static List synchronizedList(List list)
返回由指定的支持的同步(线程安全)列表 名单。为了保证串行访问,所有这一切至关重要 通过返回的列表完成对支持列表的访问。 用户必须手动同步返回的内容 迭代时列出:
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();
应该返回已经同步的列表,为什么我必须同步列表才能迭代它?
我只是在两个线程中访问列表:一个线程只添加,另一个线程获取和删除。您建议在此方案中使用哪些其他类?
感谢阅读。
答案 0 :(得分:23)
正在同步的列表仅表示add
,remove
等操作是同步的,因此是原子的。然而迭代不是,如果一个线程adds
而另一个迭代,你可能会得到一个ConcurrentModificationException。
通过手动同步迭代块,可以确保在迭代时不会修改列表。
另一种方法是使用a CopyOnWriteArrayList
提供an iterator迭代开始时迭代已知的列表,而不管后续修改。但是,如果您需要经常更改列表的内容,那么该集合效率不高。
答案 1 :(得分:-1)
起初我对这个话题感到有点困惑,因为我发现的大多数例子都没有上下文。我终于找到了这篇博文,为我解决了问题:http://netjs.blogspot.de/2015/09/how-and-why-to-synchronize-arraylist-in-java.html
从上面的示例看起来我只需要使用Collections.synchronizeList()转换我的列表然后我可以添加和删除项目而不必担心线程安全。但重要的是要注意,在将列表传递给不同的线程之前必须同步列表,否则列表访问不是互斥的。
所以一个完整的例子是:
public class SynchroProblem implements Runnable{
private List<Integer> myList;
//Constructor
public SynchroProblem(List<Integer> 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 e.g.
Iterator<Integer> iterator = myList.iterator();
while (iterator.hasNext()){
int number = iterator.next();
if (number == 123){
iterator.remove();
}
}
}
}
public static void main(String[] args) {
List<Integer> originalList = new ArrayList<Integer>();
// Synchronize list
List<Integer> 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();
}
}