Use a ReadWriteLock for a Shared Cache in Java
Problem
A read-write lock allows any number of readers at the same time but only one writer, and no readers while a write is in progress.
Protect a shared map of prices so that a writer thread and a reader thread can use it at the same time safely.
Java Program
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample {
private final Map<String, Integer> prices = new HashMap<>();
private final ReadWriteLock rw = new ReentrantReadWriteLock();
Integer get(String item) {
rw.readLock().lock(); // many readers may hold this at once
try {
return prices.get(item);
} finally {
rw.readLock().unlock();
}
}
void put(String item, int price) {
rw.writeLock().lock(); // a writer gets exclusive access
try {
prices.put(item, price);
} finally {
rw.writeLock().unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ReadWriteLockExample cache = new ReadWriteLockExample();
Thread writer = new Thread(() -> {
for (int i = 1; i <= 100; i++) cache.put("item" + (i % 5), i);
});
Thread reader = new Thread(() -> {
for (int i = 0; i < 1000; i++) cache.get("item1");
});
writer.start();
reader.start();
writer.join();
reader.join();
System.out.println("Final prices: " + new TreeMap<>(cache.prices));
}
}Output
Core Logic
Reads take the shared read lock and writes take the exclusive write lock, so frequent reads don't block each other.
- 1
getlocksrw.readLock(); several threads may hold it together. - 2
putlocksrw.writeLock(), which waits until no reader or writer holds the lock. - 3Both unlock in
finallyblocks. - 4After the threads finish, the map is printed in sorted order.
item0 is 100, so the final map shows item0=100.Key Point: Read-write locks help only when reads greatly outnumber writes; otherwise their extra bookkeeping can make them slower than a plain lock.
Key Concepts
Approach 2: Seeing Shared and Exclusive Access
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockCounts {
public static void main(String[] args) throws InterruptedException {
ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
rw.readLock().lock();
Thread secondReader = new Thread(() -> {
boolean got = rw.readLock().tryLock(); // readers don't block each other
System.out.println("Second reader got the read lock: " + got);
if (got) rw.readLock().unlock();
});
secondReader.start();
secondReader.join();
Thread writer = new Thread(() -> {
boolean got = rw.writeLock().tryLock(); // a writer must wait for all readers
System.out.println("Writer got the write lock while a reader held it: " + got);
});
writer.start();
writer.join();
rw.readLock().unlock();
System.out.println("Read locks still held: " + rw.getReadLockCount());
}
}
Output
Core Logic
Trying to acquire each kind of lock while main holds a read lock shows exactly what is shared and what is exclusive.
- 1A second reader's
tryLock()succeeds even though main holds a read lock. - 2A writer's
tryLock()fails, because a reader is still active. - 3
getReadLockCount()confirms no read locks remain once main unlocks.
true for the second reader and false for the writer.Key Point: A thread holding the read lock can't upgrade to the write lock — trying to do so deadlocks; release the read lock first.
Key Concepts
Interview Questions
What is the readers-writers problem?
Allowing many simultaneous readers while giving writers exclusive access, without starving either side. A read-write lock is the standard solution.
Is there a faster alternative for mostly-read data?
StampedLock offers optimistic reads that take no lock at all and check afterwards whether a write happened; ConcurrentHashMap is simpler still for map caches.