Skip to main content
  1. Java Concurrency (java.util.concurrent)/

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 #

View Source on GitHub

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, ReentrantLock must be explicitly unlocked. Failing to call unlock() in a finally block 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.