Mastering CopyOnWrite in Java: Boost Concurrency Performance
This article explains the CopyOnWrite optimization strategy, details the internal workings of CopyOnWriteArrayList and a custom CopyOnWriteMap in Java, outlines key implementation points, typical use cases, advantages over Vector, and discusses memory and consistency trade‑offs.
CopyOnWrite Concept
CopyOnWrite (COW) is an optimization strategy where multiple callers share the same resource until a caller attempts to modify it; at that moment the system creates a private copy for the writer while readers continue to see the original data.
Implementation of CopyOnWriteArrayList
Examining the source shows that add operations acquire a lock, copy the underlying array, insert the new element, and replace the reference, while read operations are lock‑free.
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();
}
}Reading simply returns the element from the current array without locking.
public E get(int index) {
return get(getArray(), index);
}Simple CopyOnWriteMap Example
Java does not provide a CopyOnWriteMap, but it can be built similarly to the list by copying the internal map on each write.
public class CopyOnWriteMap<K, V> implements Map<K, V>, Cloneable {
private volatile Map<K, V> internalMap;
public CopyOnWriteMap() {
internalMap = new HashMap<>();
}
public V put(K key, V value) {
synchronized (this) {
Map<K, V> newMap = new HashMap<>(internalMap);
V val = newMap.put(key, value);
internalMap = newMap;
return val;
}
}
public V get(Object key) {
return internalMap.get(key);
}
// other methods omitted for brevity
}Key Points
Implements the List interface.
Uses a ReentrantLock for write operations.
Underlying storage is a volatile array.
Writes create a new array copy; reads access the current array without locking.
Volatile Keyword
The volatile modifier forces each thread to read the latest value from main memory and write back changes, ensuring visibility across threads.
CRUD Operations
Adding, removing, updating, and retrieving elements all follow the copy‑on‑write pattern, acquiring the lock only for modifications.
// add example (simplified)
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();
}
}Typical Use Cases
CopyOnWrite containers excel in scenarios with many reads and few writes, such as whitelist/blacklist tables, configuration data, or cached reference data that is periodically refreshed.
Drawbacks
Memory consumption can double during writes because both the old and new copies coexist, which may trigger frequent full GCs for large objects. Additionally, the model only guarantees eventual consistency; readers may not see the latest write immediately.
Why CopyOnWriteArrayList Beats Vector
Vector synchronizes every method, incurring lock overhead on reads, whereas CopyOnWriteArrayList locks only on writes, giving superior read performance in read‑heavy workloads.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
