I look to java 11 implementation of .foreach method in CopyOnWriteArrayList
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
for (Object x : getArray()) {
@SuppressWarnings("unchecked") E e = (E) x;
action.accept(e);
}
}
I see that it just loops the array without any locks.
Can add() or remove() performed concurrently with foreach give a ConcurrentModificationException?
In contrast to iterator(), foreach seems to avoid using the copy of original array on write and it uses no locks.
No. You can see that from the code that it doesn't throw
ConcurrentModificationException:Note that
getArray()call is not copying the array. It is declared like this:(Since
arrayisvolatile, no lock is needed to ensure thatgetArray()returns the current version of the array.)A call to those methods will cause a new backing array to be created with the update. This is done holding a lock on the
CopyOnWriteArrayList, and then the array is replaced.Meanwhile, the
foreach()call will loop over the old array as if nothing happened.Actually,
iterator()behaves the same way asforeach. It callsgetArray()to get the current backing array.And if you look at the
COWIteratorclass, it doesn't throwConcurrentModificationExceptioneither.Note that this is all specified in the javadocs.
The javadocs for
CopyOnWriteArrayListstate:The javadocs for
foreach(inIterable) state:which is using the iterator provided by
CopyOnWriteArrayListthat doesn't throwConcurrentModificationException; see 1.However, there is a small gotcha. A sublist of a
CopyOnWriteArrayListis not aCopyOnWriteArrayList, and it can produce aConcurrentModificationException; see CopyOnWriteArrayList throwing CurrentModificationException