ReentrantLock Internals and Synchronization Mechanisms

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();
}

Tags: Java Concurrency ReentrantLock AbstractQueuedSynchronizer Thread Synchronization Locking Mechanisms

Posted on Wed, 14 Oct 2026 16:13:02 +0000 by cab