7

由于 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

4

2 回答 2

12

任何synchronized集合实现可能做的最多的事情就是保证每个单独的方法调用是同步的。但是迭代必然涉及多个单独的方法调用,因此您,同步集合的用户,必须自己同步整个迭代。

例如,如果您没有在 上同步c,则集合的内容可能会在i.hasNext()和之间变化i.next()——它甚至可能从有元素变为没有更多元素,在这种情况下i.next()会失败。

于 2013-04-03T20:59:03.260 回答
7

使类上的所有方法单独同步不会使这些方法的聚合(在组中调用它们)成为线程安全的。通过将 包装Iterator在一个同步块中,您可以保护迭代器的特定实例,使其不被多个线程调用的单个方法与其他调用穿插。

如果我.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()了更复杂的逻辑,可以进行多次调用。

更好的包装器是将它们包装在Immutable集合中,然后您保证没有其他东西可以改变调用之间的基础集合。

于 2013-04-03T21:00:36.373 回答