7

My question is about synchronizedList method Collections Class.

Javadocs say:

It is imperative that the user manually synchronize on the returned list when iterating over it:

List list = Collections.synchronizedList(new ArrayList());
      ...
synchronized(list) {
   Iterator i = list.iterator(); // Must be in synchronized block
   while (i.hasNext())
      foo(i.next());
}

Though manually synchroniziation is not required for other methods. I looked into the source code of Collections class and found shyncronization has already been taken care for all methods like add

public boolean add(E e) {
   synchronized(list) {return c.add(e);}
}

but not for iterator method. I think iterator method could have also handled synchronization in the same fashion as above method (it would have avoided the extra work i.e manual synchronization for programmers). i am sure there must be some concrete reason behind it but i am missing it?

public Iterator<E> iterator() {
   return c.iterator(); // Must be manually synched by user!
}

A way to avoid manual synchronization from Programmer

public Iterator<E> iterator() {
   synchronized(list) {
       return c.iterator(); // No need to manually synched by user!
   }
}
4

3 回答 3

17

我认为迭代器方法也可以以与上述方法相同的方式处理同步

不,绝对不能。

迭代器无法控制代码在调用其上的各个方法之间执行的操作。这才是重点。您的迭代代码将反复调用,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()

迭代器无法在对next()t=2 的调用结束和对hasNext()t=10 的调用之间进行同步。因此,如果另一个线程试图(比如说)在 t=7 时向列表中添加一个项目,那么迭代器是如何阻止它这样做的呢?

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

于 2013-09-18T13:29:41.883 回答
4

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

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

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

于 2013-09-18T13:28:49.453 回答
2

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

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

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

于 2013-09-18T14:12:17.620 回答