Understanding ReentrantLock
The ReentrantLock class in Java provides a reentrant mutual exclusion lock with extended capabilities compared to the implicit locks used with synchronized methods and statements. A reentrant lock allows a thread to acquire the lock multiple times without causing a deadlock.
Internal State Management
The core of the lock's functionality lies in a state variable that tracks the number of holdds on the lock. When a thread attempts to acquire the lock:
- If the state is zero, the lock is unheld and the thread attempts to acquire it via CAS (Compare and Set).
- If the current thread already owns the lock, the hold count is incremented.
- If acquisition fails, the thread is queued for later retry.
Base Sync Class
The Sync class extends AbstractQueuedSynchronizer and provides the foundation for lock behavior:
abstract static class Sync extends AbstractQueuedSynchronizer {
// Acquire lock logic implemented by subclasses
abstract void lock();
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
// Additional methods for condition variables, state inspection, etc.
}
Non-Fair Lock Implementation
The NonfairSync implementation attempts to acquire the lock directly before falling back to queueing:
static final class NonfairSync extends Sync {
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
Fair Lock Implementation
The FairSync implementation ensures strict FIFO ordering:
static final class FairSync extends Sync {
final void lock() {
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
Constructor Options
Users can choose between fair and non-fair implementations:
// Default non-fair constructor
public ReentrantLock() {
sync = new NonfairSync();
}
// Fair constructor option
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}