使用 CopyOnWriteArrayList 的 foreach 会导致 java 中的 ConcurrentModificationException 吗?

Can using foreach of CopyOnWriteArrayList cause ConcurrentModificationException in java?

我在 CopyOnWriteArrayList

中查看 java 11 .foreach 方法的实现
public void forEach(Consumer<? super E> action) {
    Objects.requireNonNull(action);
    for (Object x : getArray()) {
        @SuppressWarnings("unchecked") E e = (E) x;
        action.accept(e);
    }
}

我看到它只是在没有任何锁的情况下循环数组。 add()remove() 可以与 foreach 同时执行给出 ConcurrentModificationException 吗? 与 iterator() 相比,foreach 似乎避免在写入时使用原始数组的副本并且它不使用锁。

Can using foreach of CopyOnWriteArrayList cause ConcurrentModificationException in java?

没有。你可以从代码中看到它不会抛出 ConcurrentModificationException:

public void forEach(Consumer<? super E> action) {
    Objects.requireNonNull(action);
    for (Object x : getArray()) {
        @SuppressWarnings("unchecked") E e = (E) x;
        action.accept(e);
    }
}

注意 getArray() 调用不是复制数组。它是这样声明的:

private transient volatile Object[] array;

final Object[] getArray() {
    return array;
}

(因为arrayvolatile,不需要锁来确保getArray()returns是数组的当前版本。)


Can add() or remove() performed concurrently with foreach give a ConcurrentModificationException?

对这些方法的调用将导致使用更新创建新的后备数组。这是通过锁定 CopyOnWriteArrayList 完成的,然后替换数组。

与此同时,foreach() 调用将遍历 old 数组,就好像什么都没发生一样。


In contrast to iterator(), foreach seems to avoid using the copy of original array on write and it uses no locks.

实际上,iterator() 的行为方式与 foreach 相同。它调用 getArray() 来获取当前支持数组。

public Iterator<E> iterator() {
    return new COWIterator<E>(getArray(), 0);
}

如果您查看 COWIterator class,它也不会抛出 ConcurrentModificationException


请注意,这都是在 javadocs 中指定的。

  1. CopyOnWriteArrayList 状态的 javadoc:

    "... the iterator is guaranteed not to throw ConcurrentModificationException."

  2. foreach(在 Iterable)状态的 javadoc:

    "The default implementation behaves as if:"

    for (T t : this) action.accept(t);

    使用 CopyOnWriteArrayList 提供的迭代器,不会抛出 ConcurrentModificationException;见 1.


但是,有一个小问题。 CopyOnWriteArrayList 的子列表不是 CopyOnWriteArrayList,它 可以 产生 ConcurrentModificationException;见 CopyOnWriteArrayList throwing CurrentModificationException