我的问题是关于synchronizedList方法集合类。
Javadocs说:
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());
}
虽然其他方法不需要手动同步。我查看了Collections类的源代码 并发现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!
}
一种避免程序员手动同步的方法
public Iterator<E> iterator() {
synchronized(list) {
return c.iterator(); // No need to manually synched by user!
}
}
答案 0 :(得分: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()
迭代器无法在t = 2时调用next()
的结束与t = 10时调用hasNext()
之间同步。因此,如果另一个线程试图(比方说)在t = 7时将一个项添加到列表中,那么迭代器是如何阻止它这样做的呢?
这是同步集合的整体问题:每个个人操作都是同步的,而通常您希望同步一个整体操作。
答案 1 :(得分:4)
如果不同步整个迭代,另一个线程可以在迭代时修改集合,从而导致ConccurentModificationException。
此外,返回的迭代器不是线程安全的
他们可以通过将迭代器包装在一个锁定迭代器中的每个方法的SynchronizedIterator
中来解决这个问题,但这也无济于事 - 另一个线程仍然可以在两次迭代之间修改集合,并打破一切。
这是Collections.synchronized*()
方法完全无用的原因之一
有关正确的线程安全集合使用的详细信息,请参阅my blog。
答案 2 :(得分:2)
如果您想避免手动同步,则必须使用java.util.concurrent.CopyOnWriteArrayList
之类的集合。每次将对象添加到列表中时,都会复制基础数据结构以获得并发修改异常。
在您的示例中需要对Iterator进行手动序列化的原因是Iterator使用与列表相同的内部数据结构,但它们是独立的对象,并且Iterator和list都可以在任意时刻由不同的线程访问
另一种方法是制作列表的本地副本并迭代副本。