Java 需要在迭代过程中手动同步同步列表吗?

Java 需要在迭代过程中手动同步同步列表吗?,java,list,collections,synchronization,java.util.concurrent,Java,List,Collections,Synchronization,Java.util.concurrent,我的问题是关于synchronizedList方法集合类 Javadocs说: 当用户迭代返回的列表时,必须手动同步该列表: List list = Collections.synchronizedList(new ArrayList()); ... synchronized(list) { Iterator i = list.iterator(); // Must be in synchronized block while (i.hasNext()) fo

我的问题是关于synchronizedList方法集合类

Javadocs说:

当用户迭代返回的列表时,必须手动同步该列表:

List list = Collections.synchronizedList(new ArrayList());
      ...
synchronized(list) {
   Iterator i = list.iterator(); // Must be in synchronized block
   while (i.hasNext())
      foo(i.next());
}
尽管其他方法不需要手动同步。我查看了Collections类的源代码 并发现所有方法(如add)都已经注意到了shyncronization

public boolean add(E e) {
   synchronized(list) {return c.add(e);}
}
但不适用于迭代器方法。我认为迭代器方法也可以以同样的方式处理同步 正如上面的方法(它可以避免额外的工作,即程序员手动同步)。我肯定有 背后一定有什么具体的原因,但我错过了

public Iterator<E> iterator() {
   return c.iterator(); // Must be manually synched by user!
}
公共迭代器迭代器(){
返回c.iterator();//必须由用户手动同步!
}
避免程序员手动同步的方法

public Iterator<E> iterator() {
   synchronized(list) {
       return c.iterator(); // No need to manually synched by user!
   }
}
公共迭代器迭代器(){
已同步(列表){
返回c.iterator();//用户无需手动同步!
}
}

如果您不同步整个迭代,另一个线程可能会在您迭代时修改集合,从而导致ConccurentModificationException

此外,返回的迭代器不是线程安全的。
他们可以通过将迭代器包装在一个
synchronizeeditor
中来解决这个问题,该迭代器锁定迭代器中的每个方法,但这也不会有帮助——另一个线程仍然可以在两次迭代之间修改集合,并破坏一切

这就是
Collections.synchronized*()
方法完全无用的原因之一。
有关正确使用线程安全集合的详细信息,请参阅

我认为迭代器方法也可以像上面的方法一样处理同步

不,绝对不行

迭代器无法控制代码在调用其上的各个方法之间执行的操作。这就是重点。您的迭代代码将反复调用
hasNext()
next()
,在这些调用期间进行同步是可行的,但并不相关-重要的是,在您整个迭代过程中,没有其他代码试图修改列表

因此,想象一个时间表:

t = 0: call iterator()
t = 1: call hasNext()
t = 2: call next()
// Do lots of work with the returned item
t = 10: call hasNext()
迭代器无法在t=2时对
next()
的调用结束与t=10时对
hasNext()的调用结束之间进行同步。因此,如果另一个线程试图(比如)在t=7时向列表中添加一个项,迭代器是如何阻止它这样做的


这是同步集合的总体问题:每个单独的操作都是同步的,而通常您希望同步整个chunky操作。

如果要避免手动同步,您必须使用类似
java.util.concurrent.CopyOnWriteArrayList的集合。每次将对象添加到列表中时,底层数据结构都会被复制到avaoid,以避免并发修改异常

在示例中,需要对迭代器进行手动序列化的原因是迭代器使用与列表相同的内部数据结构,但它们是独立的对象,并且迭代器和列表都可以在任意时刻由不同的线程访问


另一种方法是制作列表的本地副本并在副本上迭代。

@StevenYang:因为这不会阻止在调用之间进行更改。基本上,再次仔细阅读答案:)