Understanding AbstractQueuedSynchronizer (AQS) and ReentrantLock in Java

The article explains Java’s AbstractQueuedSynchronizer as the foundation of many concurrency utilities, details how ReentrantLock leverages AQS’s FIFO CLH queue, state field, and lock acquisition/release mechanisms, compares it with synchronized, and demonstrates building a custom lock with AQS.

Meituan Technology Team
Meituan Technology Team
Meituan Technology Team
Understanding AbstractQueuedSynchronizer (AQS) and ReentrantLock in Java

This article explains the core concepts of Java's AbstractQueuedSynchronizer (AQS) and how ReentrantLock is built on top of it. It starts with an overview of AQS as the foundation for many synchronization utilities in the java.util.concurrent package.

1. ReentrantLock Features

ReentrantLock is a re‑entrant exclusive lock that can be configured as fair or non‑fair. The article compares its usage with the built‑in synchronized keyword, showing typical code patterns for both.

// Synchronized usage
synchronized (this) {}
public synchronized void test() {}
// ReentrantLock usage
ReentrantLock lock = new ReentrantLock(true);
lock.lock();
try { /* critical section */ } finally { lock.unlock(); }

2. AQS Architecture

AQS provides a FIFO CLH‑based wait queue and a volatile int state field that represents the synchronization state. The article presents the five‑layer architecture of AQS, from the public API down to low‑level atomic operations.

// AQS state field
private volatile int state;

3. Lock Acquisition Process

When a thread attempts to acquire a lock, AQS calls tryAcquire. If it fails, the thread is enqueued via addWaiter and then repeatedly invokes acquireQueued until it can acquire the lock.

// AQS acquire method
public final void acquire(int arg) {
    if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

The addWaiter method creates a new Node representing the thread and attempts a fast‑path insertion at the tail of the queue. If the fast path fails, it falls back to the full enq algorithm.

// Fast‑path enqueue
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;
}

If the thread is the head's successor and tryAcquire succeeds, it becomes the new head; otherwise it may be parked using LockSupport.park until it is unparked by its predecessor.

// Parking and interrupt check
private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}

4. Queue Management and Cancellation

The article details how nodes transition to the CANCELLED state, how the queue is cleaned up, and why the prev pointer is rarely updated during cancellation to avoid race conditions.

// Cancel a waiting node
private void cancelAcquire(Node node) {
    if (node == null) return;
    node.thread = null;
    Node pred = node.prev;
    while (pred.waitStatus > 0) // skip cancelled predecessors
        node.prev = pred = pred.prev;
    Node predNext = pred.next;
    node.waitStatus = Node.CANCELLED;
    // ... (tail adjustment and successor unpark logic) ...
}

5. Lock Release

Releasing a lock invokes release in AQS, which calls the subclass's tryRelease. If the lock becomes free, the head node's successor is unparked.

// AQS release method
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

The concrete tryRelease for ReentrantLock checks ownership, decrements the state, and clears the owner when the count reaches zero.

// ReentrantLock tryRelease
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = (c == 0);
    if (free) setExclusiveOwnerThread(null);
    setState(c);
    return free;
}

6. Custom Synchronizer Example

To illustrate AQS usage, the article provides a minimal custom lock implementation named LeeLock. It defines an inner Sync class extending AbstractQueuedSynchronizer and overrides tryAcquire, tryRelease, and isHeldExclusively. The public lock and unlock methods delegate to sync.acquire(1) and sync.release(1) respectively.

public class LeeLock {
    private static class Sync extends AbstractQueuedSynchronizer {
        @Override protected boolean tryAcquire(int arg) {
            return compareAndSetState(0, 1);
        }
        @Override protected boolean tryRelease(int arg) {
            setState(0);
            return true;
        }
        @Override protected boolean isHeldExclusively() {
            return getState() == 1;
        }
    }
    private final Sync sync = new Sync();
    public void lock() { sync.acquire(1); }
    public void unlock() { sync.release(1); }
}

A test program creates two threads that increment a shared counter under LeeLock, demonstrating correct synchronization (final count = 20000).

7. Summary

The article provides a deep dive into AQS's internal mechanisms—queue management, state handling, and interaction with ReentrantLock—offering readers a solid foundation for understanding Java's concurrency utilities and for building custom synchronizers.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaconcurrencySynchronizationAQSReentrantLock
Meituan Technology Team
Written by

Meituan Technology Team

Over 10,000 engineers powering China’s leading lifestyle services e‑commerce platform. Supporting hundreds of millions of consumers, millions of merchants across 2,000+ industries. This is the public channel for the tech teams behind Meituan, Dianping, Meituan Waimai, Meituan Select, and related services.

0 followers
Reader feedback

How this landed with the community

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.