ReadWriteLock & StampedLock in Java
Many readers or one writer; optimistic reads.
Many readers OR one writer
A ReentrantReadWriteLock has two locks. The read lock is shared: any number of readers can hold it at once, never blocking each other. The write lock is exclusive: no readers and no other writers. Readers only need to exclude writers.
var rw = new ReentrantReadWriteLock();
rw.readLock().lock(); // many at once
try { return cache.get(k); }
finally { rw.readLock().unlock(); }Your turn
main holds the write lock. Can it also take the read lock?
var rw = new ReentrantReadWriteLock();
rw.writeLock().lock();
System.out.println(rw.readLock().tryLock());truefalseThrows IllegalMonitorStateException
Show the answer
true. The writer already excludes everyone else, so it may also take the read lock. That's how you downgrade: acquire read, then release write. The opposite direction is the trap on the next screen.
No upgrades
Holding a read lock, you can't upgrade to the write lock. writeLock().tryLock() returns false, and writeLock().lock() would wait forever for readers to leave, including yourself. Release the read lock first; once it's free, the write lock can be taken.
rw.readLock().lock();
rw.writeLock().tryLock(); // false!
rw.readLock().unlock();
rw.writeLock().tryLock(); // trueStampedLock: optimistic reads
StampedLock adds a cheaper path. tryOptimisticRead() returns a stamp without locking. Read your fields, then validate(stamp): true means no write happened since. If false, fall back to a real read lock.
long s = sl.tryOptimisticRead();
double cx = x, cy = y;
if (!sl.validate(s)) {
s = sl.readLock();
try { cx = x; cy = y; }
finally { sl.unlockRead(s); }
}Is the stamp still good?
You get a stamp from tryOptimisticRead(). Another thread then acquires and releases the write lock. What does validate(stamp) return?
Think about it, then reveal the answer
false. Any write-lock acquisition after the stamp invalidates it, even if that write lock has already been released. That's the signal to re-read your data under a real read lock.
StampedLock is not reentrant
Unlike ReentrantReadWriteLock, StampedLock isn't reentrant: a thread that already holds its write lock and asks again blocks itself forever. It also has no Conditions. Keep its critical sections small and simple.
Measure first
Read-write locks pay off only when reads vastly outnumber writes: config caches, routing tables, reference data. With balanced traffic, their bookkeeping can make them slower than a plain lock. Profile first, then switch.
Key takeaways
- Read lock: shared. Write lock: exclusive
- Pays off only when reads vastly outnumber writes
- tryOptimisticRead() + validate(stamp) avoids locking on reads
- StampedLock isn't reentrant — re-locking can deadlock yourself
StampedLock's own javadoc demonstrates optimistic reading with a tiny 2D Point class and a distanceFromOrigin() method.
Practice questions
What does this print?
var rw = new ReentrantReadWriteLock();
rw.readLock().lock();
System.out.println(rw.writeLock().tryLock());
rw.readLock().unlock();
System.out.println(rw.writeLock().tryLock());- false true
- true true
- true false
- false false
Check your answer
false true. You can't upgrade a held read lock to a write lock, so the first tryLock() fails. Once the read lock is released, the write lock is free.
What does this print?
var sl = new StampedLock();
long s = sl.tryOptimisticRead();
System.out.println(sl.validate(s));
long w = sl.writeLock();
sl.unlockWrite(w);
System.out.println(sl.validate(s));- true false
- true true
- false false
- false true
Check your answer
true false. The stamp stays valid until a write lock is acquired. After the write, validate(s) is false, telling the reader to retry under a real read lock.