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.
CodePudding user response:
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
(inIterable
) 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 throwConcurrentModificationException
; 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