由于c已经是同步集合,因此它是线程安全的。但为什么我们必须再次使用synchronized(c)
进行迭代?真的很困惑。感谢。
“用户必须手动同步返回的内容 迭代时收集:
Collection c = Collections.synchronizedCollection(myCollection);
...
synchronized(c) {
Iterator i = c.iterator(); // Must be in the synchronized block
while (i.hasNext()) {
foo(i.next());
}
}
不遵循此建议可能会导致非确定性行为。 “ http://docs.oracle.com/javase/6/docs/api/java/util/Collections
答案 0 :(得分:10)
大多数synchronized
集合实现可能会保证每个单个方法调用都是同步的。但迭代必然涉及多个单独的方法调用,因此您(同步集合的用户)必须自己在整个迭代中进行同步。
例如,如果您未在c
上进行同步,则集合的内容可能会在i.hasNext()
和i.next()
之间发生变化 - 甚至可能会从元素变为无效更多元素,在这种情况下i.next()
会失败。
答案 1 :(得分:4)
使类上的所有方法单独同步不会使这些方法的聚合(在组中调用它们)是线程安全的。通过将Iterator
包装在一个synchronized块中,您可以保护迭代器的特定实例,使其单独的方法被多个线程散布其他调用。
如果我在安全时拨打.add()
,如果我需要多次拨打.add()
来完成逻辑陈述,则无法保证其他人没有添加其他内容或删除某些内容我的.add()
调用之间的其他操作,除非我阻止通过.add()
(或任何其他方法)对表示集合的变量synchronizing
调用其他所有内容。
Iterator
对集合上的各个方法进行多次调用,它们都必须包含在单个synchronized
块中,以使它们作为单个transaction
排序执行。检查Iterator
实现的源代码,你会明白我的意思。以下是List
的源代码,它对底层实现进行多次单独调用,因此它们都需要由同一个线程以不间断的顺序执行才能确定。
@Override
public Iterator<A> iterator() {
if (tail == null)
return emptyIterator();
return new Iterator<A>() {
List<A> elems = List.this;
public boolean hasNext() {
return elems.tail != null;
}
public A next() {
if (elems.tail == null)
throw new NoSuchElementException();
A result = elems.head;
elems = elems.tail;
return result;
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
AbstractList.iterator()
的{{3}}显示了进行多次调用的更复杂的逻辑。
更好的包装器将它们包装在Immutable
个集合中,然后你保证没有其他任何东西可以改变调用之间的底层集合。