Explicit Locks: ReentrantLock, ReadWriteLock, StampedLock — Java Interview Guide | Cracked Java
Senior

Explicit Locks: ReentrantLock, ReadWriteLock, StampedLock

The java.util.concurrent.locks toolbox — tryLock, fairness, Condition, read/write separation, optimistic reads, and the AbstractQueuedSynchronizer underneath.

Prereqs: synchronization, jmm-happens-before

synchronized is convenient but rigid: you cannot interrupt a thread waiting for it, you cannot time out, and you cannot release it in a different scope than you acquired it. The java.util.concurrent.locks package trades that convenience for control — explicit Lock objects you acquire and release by hand, with the iron rule that every lock() is paired with an unlock() in a finally block.

The three locks

ReentrantLock is the direct, more powerful replacement for synchronized: same mutual-exclusion and reentrancy semantics, plus tryLock, interruptible acquisition, timed acquisition, optional fairness, and multiple Condition objects per lock.

ReentrantReadWriteLock splits one logical lock into a read lock (shared — many readers at once) and a write lock (exclusive). It pays off only when reads vastly outnumber writes and the critical section is non-trivial.

StampedLock (Java 8+) adds a third, cheaper mode: optimistic reading. A read returns a long stamp instead of blocking; you do your work, then call validate(stamp) to check no writer interfered. No writer contention means no cache-line ping-pong.

ReentrantLock        : [ exclusive ]                 1 holder
ReadWriteLock        : [ many readers ] XOR [ writer ]
StampedLock          : optimistic read (no lock) -> validate
                     read lock | write lock (pessimistic)
Lock modes and concurrency

AQS underneath

All three are built on AbstractQueuedSynchronizer (AQS): an int state guarded by CAS plus a FIFO wait queue of parked threads. ReentrantLock stores hold-count in state; the read/write locks pack reader and writer counts into the high and low 16 bits. Understanding AQS is what lets you reason about fairness, barging, and why these locks scale the way they do.

When to reach for them

Default to synchronized or java.util.concurrent collections — they are simpler and the JVM optimizes them well. Reach for explicit locks when you genuinely need a feature synchronized lacks: timed/interruptible acquisition, try-then-back-off to dodge deadlock, fairness, read/write separation, or several wait sets on one lock via Condition.

Questions

7 in this topic