This will, right now, answer how CopyOnWriteArrayList avoids the need for a ConcurrentModificationException.
When you modify the collection the CopyOnWriteArrayList does two things
- It prevents other threads from modifying the collection via locking
- Copies all the elements in the current CopyOnWriteArrayList into a new array and then assigns that new array to the class's array instance
So how does that prevent a CME? A CME in standard collections will only be thrown as a result of iterating. The exception gets thrown if, while iterating over the collection, an add or remove is executed on the same collection instance.
The CopyOnWriteArrayList's iterator assigns the current array as a final field snapshot of the collection and uses that for iteration. If another thread (or even the same thread) tries to add to the CopyOnWriteArrayList then updates will be applied to a new copy and not the snapshot one we are currently iterating.
For instance, we know the add method looks like
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
Notice the thread local newElements assignment being made, when that is completed it will set to the class instance volatile
array.
Then comes the iterator, it's defined as
static final class COWIterator<E> implements ListIterator<E> {
/** Snapshot of the array */
private final Object[] snapshot;
/** Index of element to be returned by subsequent call to next. */
private int cursor;
So when iterating, we are reading whatever was the array prior to any modifications, and since no other thread can modify the snapshot we are looking at a ConcurrentModificationException cannot happen.