Fundamentals 14 min read

Understanding CopyOnWriteArrayList, Vector, and SynchronizedList in Java Concurrency

This article explains the copy‑on‑write optimization, compares Vector, Collections.synchronizedList and CopyOnWriteArrayList, analyzes their fail‑fast behavior, shows how CopyOnWriteArrayList is implemented and iterated, and presents performance benchmarks highlighting their strengths and weaknesses.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Understanding CopyOnWriteArrayList, Vector, and SynchronizedList in Java Concurrency

Preface

Copy‑on‑write (COW) is an optimization strategy in computer programming where multiple callers share the same resource until one attempts to modify it, at which point a private copy is created.

The main advantage is that if callers never modify the resource, no extra copies are made, allowing read‑only sharing.

Vector and synchronizedList

Vector is a thread‑safe container because almost every method is declared synchronized . Collections.synchronizedList(new ArrayList()) achieves the same safety by wrapping an ArrayList and synchronizing each method internally.

Even though these containers are thread‑safe, using an iterator while another thread modifies the collection still throws java.util.ConcurrentModificationException . The failure is caused by the fail‑fast mechanism that compares expectedModCount (captured when the iterator is created) with the list's modCount (incremented on each structural change).

@Test
public void testVectorConcurrentReadWrite() {
    Vector
vector = new Vector<>();
    vector.add(1);
    vector.add(2);
    vector.add(3);
    vector.add(4);
    vector.add(5);
    for (Integer item : vector) {
        new Thread(vector::clear).start();
        System.out.println(item);
    }
}

Because each thread gets its own iterator, expectedModCount is thread‑local. When one thread clears the vector, modCount increments, but the other thread's iterator still expects the old value, triggering the exception.

The same issue exists for synchronizedList ; the source code even warns that manual locking is required when iterating.

public synchronized Iterator
iterator() {
    // Itr is AbstractList's private inner class
    return new Itr();
}

Therefore, to avoid the exception, the collection must be locked before iteration:

synchronized (vector) {
    for (int i = 0; i < vector.size(); i++) {
        System.out.println(vector.get(i));
    }
}
// or
synchronized (vector) {
    for (Integer item : vector) {
        System.out.println(item);
    }
}

CopyOnWriteArrayList Introduction

CopyOnWriteArrayList (and its Set counterpart) are modern concurrent containers that replace older thread‑safe classes like Vector. They achieve thread safety with finer‑grained locking, using CAS, volatile fields, and a snapshot‑based iterator that never throws ConcurrentModificationException .

Implementation Principle

Copy‑on‑write separates reads from writes: a write creates a new array copy, modifies it, and then atomically updates the reference. Reads always operate on the current immutable array, so they require no locking.

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();
    }
}

final void setArray(Object[] a) {
    array = a;
}

public E get(int index) {
    return get(getArray(), index);
}

final Object[] getArray() {
    return array;
}

Iteration – COWIterator

The iterator returned by CopyOnWriteArrayList.iterator() holds a snapshot of the array at the moment of creation. Modifications create a new array, leaving the snapshot unchanged, which is why no ConcurrentModificationException is thrown.

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

Performance Comparison

Benchmark tests show that write operations on CopyOnWriteArrayList are much slower (over 30×) than on a synchronized list because each write copies the entire array. However, read operations are about twice as fast because they are lock‑free.

@Test
public void testThreadSafeListWrite() {
    List
copyOnWriteArrayList = new CopyOnWriteArrayList<>();
    List
synchronizedList = Collections.synchronizedList(new ArrayList<>());
    // ... parallel writes using ThreadLocalRandom ...
}

@Test
public void testThreadSafeListRead() {
    // ... populate both lists with 1,000,000 integers ...
    // ... parallel reads using ThreadLocalRandom ...
}

Pros and Cons of CopyOnWriteArrayList

Advantages

Ideal for read‑heavy, write‑light scenarios (e.g., configuration, blacklist) because reads are lock‑free and highly concurrent.

Better concurrent performance than Vector, as only write operations acquire a lock.

Disadvantages

Only provides eventual consistency; iterators may see stale data if a concurrent write occurs.

High memory overhead when writes are frequent or elements are large, potentially causing GC pressure.

For workloads with frequent modifications, other concurrent containers such as ConcurrentHashMap may be more appropriate.

JavaperformanceconcurrencycollectionscopyonwritearraylistThreadSafety
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.