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

我在 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 并发执行吗?与 iterator() 相比,foreach 似乎避免在写入时使用原始数组的副本并且它不使用锁。

回答1

使用 CopyOnWriteArrayListforeach 会导致 java 中的 ConcurrentModificationException 吗?

不。您可以从代码中看到它不会抛出 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() 返回数组的当前版本。)

add()remove() 可以与 foreach 并发执行吗?

对这些方法的调用将导致使用更新创建一个新的支持数组。这是在 CopyOnWriteArrayList 上保持锁定完成的,然后替换数组。

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

iterator() 相比,foreach 似乎避免在写入时使用原始数组的副本,并且它不使用锁。

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

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

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

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

  1. CopyOnWriteArrayList 状态的 javadocs:

    "... 保证迭代器不会抛出 ConcurrentModificationException。"

  2. foreach 的 javadocs (in Iterable) 状态:

    "默认实现的行为就像:"

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

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

但是,有一个小问题。 CopyOnWriteArrayList 的子列表不是 CopyOnWriteArrayList,它可以产生 ConcurrentModificationException;请参阅 https://stackoverflow.com/questions/1527519

相似文章

最新文章