LESSON 9.4
Read-write locks and immutability
Maximize throughput on read-heavy data structures with Read-Write Locks (shared read, exclusive write) and eliminate concurrency hazards via Immutability.
6 min read
In read-heavy applications—such as caching layers, routing tables, and feature flag registries—hundreds of threads read state simultaneously, while writes occur only occasionally. If a single exclusive lock (synchronized or standard mutex) guards the data structure, all readers block each other sequentially, destroying multi-core CPU parallelism.
The problem
// Severe bottleneck: readers block other readers unnecessarily
class MonolithicCache {
private final Map<String, String> map = new HashMap<>();
// 100 threads calling get() will all queue up behind 1 thread!
public synchronized String get(String key) {
return map.get(key); // Exclusive lock on pure read!
}
public synchronized void put(String key, String val) {
map.put(key, val);
}
}The pattern
1. Read-Write Lock
A Read-Write Lock (ReentrantReadWriteLock) separates read access from write access:
- Read Lock: Shared. Multiple reader threads can hold the lock simultaneously as long as no writer is active.
- Write Lock: Exclusive. Only one writer thread can hold the lock; all readers and other writers block.
2. Immutability
An alternative that eliminates locks completely is Immutability. An immutable object (or copy-on-write structure) never mutates after creation. Writers replace the reference with a new immutable snapshot, allowing readers to read safely without acquiring any locks.
In code
When to use it, and when not to
Use Read-Write Locks when:
- The frequency of read operations is significantly higher than write operations (e.g. 10:1 or 100:1).
- The read operations take significant time (e.g. searching, hashing, parsing data structures).
Use Immutability when:
- Data objects are small-to-medium in size and copying is cheap.
- You want to pass state across threads, actors, or event streams without any risk of side effects.
Don't use Read-Write Locks when:
- Writes are frequent: lock management overhead in
ReentrantReadWriteLockexceeds standardReentrantLockperformance under heavy write contention.
Where you have already seen it
In Java, java.util.concurrent.CopyOnWriteArrayList uses immutability for thread-safe iteration without locking. In React and Redux, state trees are strictly immutable: reducers return new state snapshots ({ ...state, count: 1 }), enabling fast reference comparisons (oldState === newState). In Linux kernel development, RCU (Read-Copy-Update) is the low-level OS equivalent.
Related patterns
- Immutability and Memento: Immutable state snapshots act as ready-made Mementos for time-travel debugging.
- Read-Write Lock and Double-Checked Locking: Read-write locking is often paired with lazy singleton publication.
Try it
Versioned Key-Value Store LLD
Implement a versioned key-value store using copy-on-write snapshots for lock-free point-in-time reads.