AQS Made Simple: How Java’s Locking Framework Works

AbstractQueuedSynchronizer (AQS) is a foundational framework in Java for building locks and synchronizers like ReentrantLock, CountDownLatch, Semaphore, etc. It manages a FIFO queue and a state variable to handle synchronization.

Its primary goal is to simplify the development of robust and scalable synchronization tools.

With the introduction of Virtual Threads in Java 21, traditional blocking locks (like ReentrantLock) are being re-evaluated in favor of non-blocking constructs or high-throughput alternatives like StampedLock.

Core Mechanism of AQS

State Management

AQS maintains an internal state using a volatile int.

private volatile int state;

AQS provides protected methods for accessing and modifying this state.

protected final int getState();
protected final void setState(int newState);
protected final boolean compareAndSetState(int expect, int update);

These are typically used by subclasses to control synchronization logic.

Methods to Override

When extending AQS, developers implement the following protected methods to define custom locking behavior.

protected boolean tryAcquire(int arg);
protected boolean tryRelease(int arg);
protected int tryAcquireShared(int arg);
protected boolean tryReleaseShared(int arg);
protected boolean isHeldExclusively();

These methods are called by higher-level functions like acquire() and release() to control how threads access shared resources.

ReentrantLock Example

protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

final boolean nonfairTryAcquire(int acquires) {
    // Get the current thread
    final Thread current = Thread.currentThread();

    // Get the current state value, which represents the lock status
    int c = getState();

    // If the state is 0, it means the lock is currently free
    if (c == 0) {
        // Attempt to atomically set the state to `acquires` (typically 1)
        if (compareAndSetState(0, acquires)) {
            // Set the current thread as the exclusive owner of the lock
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // If the state is not 0, the lock is already held by another thread
    // Check if the current thread is the one holding the lock (reentrant case)
    else if (current == getExclusiveOwnerThread()) {
        // If so, allow reentrancy by incrementing the state
        int nextc = c + acquires;

        // Overflow check (not likely, but needed for safety)
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");

        // Update the state with the new value
        setState(nextc);
        return true;
    }

    // Failed to acquire the lock
    return false;
}

Benefits

Relying on blocking, AQS will be becoming less popular as Virtual Threads gradually offer a faster and more efficient way to handle concurrency without blocking.