Backend Development 27 min read

Understanding ReentrantLock and AQS: A Deep Dive into Java Lock Implementation

This article provides a comprehensive walkthrough of Java's ReentrantLock implementation, covering linked list and queue data structures, the AbstractQueuedSynchronizer (AQS) framework, lock acquisition and release mechanisms, fair vs. non-fair locks, and detailed code analysis with illustrative diagrams.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Understanding ReentrantLock and AQS: A Deep Dive into Java Lock Implementation

Introduction

The article begins with a brief motivation to review linked list and queue data structures before diving into the source code of ReentrantLock .

Data Structures

Linked List

Explains singly, doubly, and circular linked lists using simple analogies, and shows diagrams of each structure.

Queue

Describes the FIFO nature of queues with everyday examples and includes an illustration.

AQS Queue Synchronizer

Introduces AbstractQueuedSynchronizer (AQS) as the core abstraction behind ReentrantLock , listing its key fields and abstract methods.

public abstract class AbstractQueuedSynchronizer {
    private volatile Node head;
    private volatile Node tail;
    private volatile int state;
    // ... other fields and methods ...
    protected abstract boolean tryAcquire(int arg);
    protected abstract boolean tryRelease(int arg);
    // ...
}

ReentrantLock Lock Process

Details the lock acquisition flow: construction of FairSync or NonfairSync , the lock() method delegating to sync.lock() , and the acquire algorithm that first calls tryAcquire and, if it fails, enqueues the thread.

public void lock() {
    sync.lock();
}

private static final class Sync extends AbstractQueuedSynchronizer {
    final void lock() { acquire(1); }
    // abstract void lock(); implemented by FairSync / NonfairSync
}

private static final class FairSync extends Sync {
    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;
    }
}

The addWaiter method creates a Node for the current thread and links it into the queue; enq initializes the queue when it is empty.

private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    enq(node);
    return node;
}

private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        if (t == null) {
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

If the thread is not at the head of the queue, acquireQueued repeatedly checks its predecessor and may park the thread via LockSupport.park .

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

ReentrantLock Unlock Process

Unlock delegates to sync.release(1) , which calls tryRelease to decrement the state and, when the state reaches zero, clears the owner and wakes the next waiting thread.

public void unlock() { sync.release(1); }

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

The unparkSuccessor method wakes the first waiting thread after a successful release.

private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0) s = t;
    }
    if (s != null)
        LockSupport.unpark(s.thread);
}

Additional Details

The article also explains hasQueuedPredecessors , which determines whether a thread must wait, and shouldParkAfterFailedAcquire , which sets the predecessor’s wait status to SIGNAL before parking.

public final boolean hasQueuedPredecessors() {
    Node t = tail, h = head;
    Node s;
    return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());
}

Conclusion

Mastering ReentrantLock requires careful study of the AQS framework, the lock‑acquisition algorithm, and the subtle interactions of queue nodes; such low‑level knowledge is essential for backend developers who need precise control over concurrency.

JavaconcurrencyData StructuresLockAQSReentrantLock
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.