ReentrantLock
2 mins
ReentrantLock is the most important lock implementation in the java.util.concurrent.locks package. It provides the same basic semantics as the implicit synchronized keyword but with significantly more capabilities.
Source Code #
Implementation Mechanism #
ReentrantLock is a classic application of AbstractQueuedSynchronizer (AQS) in Exclusive Mode. The state variable represents the “hold count” of the lock. When a thread calls lock(), it tries to increment the state via CAS. If the state is already non-zero and the current thread is the owner, the state is incremented (reentrancy).
abstract static class Sync extends AbstractQueuedSynchronizer {
// Non-fair tryLock in ReentrantLock.Sync
final boolean tryLock() {
Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (getExclusiveOwnerThread() == current) {
if (++c < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(c);
return true;
}
return false;
}
}
Canonical Usage #
When to use: Use ReentrantLock when you need advanced locking capabilities such as fairness, interruptible waiting, timed waiting, or multiple Condition variables.
Common Patterns:
- Fairness Policy: Ensuring that the longest-waiting thread acquires the lock next.
- Interruptible Locking: Allowing a thread to stop waiting if it is interrupted.
- Multiple Conditions: Creating separate waiting queues for different events (e.g., a “not empty” and “not full” condition for a queue).
ReentrantLock lock = new ReentrantLock(true); // Fair lock
Condition condition = lock.newCondition();
public void doWork() throws InterruptedException {
lock.lockInterruptibly(); // Allows interruption while waiting
try {
while (!ready) {
condition.await(); // Wait for a specific signal
}
// Perform critical section work
} finally {
lock.unlock(); // Always unlock in finally!
}
}
Resilience and Safety #
- Mandatory Unlock: Unlike
synchronized,ReentrantLockmust be explicitly unlocked. Failing to callunlock()in afinallyblock is a common cause of deadlocks. - Reentrancy: A thread can acquire the same lock multiple times without deadlocking itself, provided it releases the lock the same number of times.