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.
Can using
foreach
ofCopyOnWriteArrayList
causeConcurrentModificationException
in java?
No. You can see that from the code that it doesn't throw ConcurrentModificationException
:
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
for (Object x : getArray()) {
@SuppressWarnings("unchecked") E e = (E) x;
action.accept(e);
}
}
Note that getArray()
call is not copying the array. It is declared like this:
private transient volatile Object[] array;
final Object[] getArray() {
return array;
}
(Since array
is volatile
, no lock is needed to ensure that getArray()
returns the current version of the array.)
Can
add()
orremove()
performed concurrently withforeach
give aConcurrentModificationException
?
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.
In contrast to
iterator()
, foreach seems to avoid using the copy of original array on write and it uses no locks.
Actually, iterator()
behaves the same way as foreach
. It calls getArray()
to get the current backing array.
public Iterator<E> iterator() {
return new COWIterator<E>(getArray(), 0);
}
And if you look at the COWIterator
class, it doesn't throw ConcurrentModificationException
either.
Note that this is all specified in the javadocs.
The javadocs for CopyOnWriteArrayList
state:
"... the iterator is guaranteed not to throw
ConcurrentModificationException
."
The javadocs for foreach
(in Iterable
) state:
"The default implementation behaves as if:"
for (T t : this) action.accept(t);
which is using the iterator provided by CopyOnWriteArrayList
that doesn't throw ConcurrentModificationException
; see 1.
However, there is a small gotcha. A sublist of a CopyOnWriteArrayList
is not a CopyOnWriteArrayList
, and it can produce a ConcurrentModificationException
; see CopyOnWriteArrayList throwing CurrentModificationException