# How AQS AbstractQueuedSynchronizer Powers Java Concurrency Primitives

> Learn how AbstractQueuedSynchronizer AQS powers Java concurrency primitives like ReentrantLock and Semaphore. Understand its queue and state variable for exclusive and shared acquisition.

- Repository: [CyC2018/CS-Notes](https://github.com/CyC2018/CS-Notes)
- Tags: internals
- Published: 2026-02-24

---

**AbstractQueuedSynchronizer (AQS) provides a framework that manages a FIFO wait-queue and an integer state variable to enable high-level Java concurrency primitives such as `ReentrantLock`, `CountDownLatch`, and `Semaphore` through exclusive and shared acquisition modes.**

The `java.util.concurrent` (J.U.C) package relies heavily on AQS to implement its synchronization utilities. According to the concurrency notes in the [CyC2018/CS-Notes](https://github.com/CyC2018/CS-Notes) repository, this framework abstracts the mechanics of blocking threads, queuing waiters, and atomic state management so that concrete classes can define only the logic for acquiring and releasing resources.

## Core Architecture of AQS AbstractQueuedSynchronizer

### The State Variable and CLH Queue

At the heart of AQS AbstractQueuedSynchronizer is a volatile `int` **state** field that represents the synchronization status. For a mutex, this might toggle between 0 and 1; for a semaphore, it tracks available permits. AQS organizes waiting threads into a **CLH (Craig, Landin, and Hagersten) lock queue**, a variant of a FIFO queue where each thread is encapsulated in a `Node` object.

When a thread fails to acquire the resource, AQS invokes `acquire()` or `acquireShared()`, creates a `Node` for the thread, appends it to the queue tail using CAS operations, and **parks** the thread using `LockSupport.park()`. This avoids busy-waiting and delegates thread scheduling to the JVM.

### Acquisition and Release Protocols

The framework defines template methods that subclasses must implement:

- **`tryAcquire(int arg)`**: Attempts to acquire the exclusive resource. Returns `true` if successful.
- **`tryAcquireShared(int arg)`**: Attempts to acquire the resource in shared mode. Returns a non-negative value if successful.
- **`tryRelease(int arg)`** and **`tryReleaseShared(int arg)`**: Update the state to release the resource.

When release methods succeed, AQS automatically **unparks** the successor node in the CLH queue, allowing that thread to retry acquisition. This handshake between `release()` and `acquire()` ensures FIFO fairness while maintaining high throughput.

## Exclusive vs. Shared Synchronization Modes

### Exclusive Mode Implementation

In **exclusive** mode, only one thread may hold the resource at a time. `ReentrantLock` uses this pattern by extending AQS through an inner `Sync` class. When `lock()` is called, AQS invokes `tryAcquire(1)`, which checks if the state is 0 (unlocked) and performs a CAS to 1. If the state is non-zero and the owner is the current thread, `ReentrantLock` increments the hold count to support reentrancy.

```java
// ReentrantLock – exclusive mode built on AQS
Lock lock = new ReentrantLock();
Runnable task = () -> {
    lock.lock();                           // AQS attempts exclusive acquire via tryAcquire
    try {
        System.out.println(Thread.currentThread().getName() + " holds lock");
        Thread.sleep(500);
    } catch (InterruptedException ignored) {}
    finally {
        lock.unlock();                     // AQS releases state and unparks next waiter
    }
};
ExecutorService exec = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) exec.execute(task);
exec.shutdown();

```

### Shared Mode Implementation

**Shared** mode allows multiple threads to access the resource concurrently. Implementations like `CountDownLatch` and `Semaphore` override `tryAcquireShared(int arg)`. In `CountDownLatch`, the initial state represents the count; `tryAcquireShared` returns 1 only when the state reaches 0, allowing all waiting threads to proceed simultaneously.

```java
// CountDownLatch – uses AQS in shared mode
CountDownLatch latch = new CountDownLatch(3);
ExecutorService exec = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
    exec.execute(() -> {
        System.out.println(Thread.currentThread().getName() + " doing work");
        latch.countDown();                 // releases a permit in AQS (decrements state)
    });
}
latch.await();                           // acquires shared permit (blocks until count == 0)
System.out.println("All work done");
exec.shutdown();

```

`Semaphore` uses shared mode to decrement permit counts. When `acquire()` is called, `tryAcquireShared` attempts to subtract 1 from the state; if the result is non-negative, the acquisition succeeds.

```java
// Semaphore – AQS manages a permit counter
Semaphore sem = new Semaphore(2);          // at most 2 threads may proceed
ExecutorService exec = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
    exec.execute(() -> {
        try {
            sem.acquire();                // AQS tries to decrement state; may block if 0
            System.out.println(Thread.currentThread().getName() + " acquired");
            Thread.sleep(1000);            // simulate work
        } catch (InterruptedException e) { }
        finally {
            sem.release();                // AQS increments state and unparks waiting thread
        }
    });
}
exec.shutdown();

```

## How J.U.C Primitives Use AQS AbstractQueuedSynchronizer

The `java.util.concurrent` primitives delegate synchronization logic to AQS through specialized inner classes. The following table maps each primitive to its AQS implementation details as documented in `notes/Java 并发.md`:

| Primitive | AQS Subclass | Synchronization Mode | State Management |
|-----------|--------------|----------------------|------------------|
| **`ReentrantLock`** | `AbstractQueuedSynchronizer` (inner `Sync`) | Exclusive | 0/1 state with reentrant hold count |
| **`CountDownLatch`** | `AbstractQueuedSynchronizer` (inner `Sync`) | Shared | `tryAcquireShared` returns positive only when state == 0 |
| **`Semaphore`** | `AbstractQueuedSynchronizer` (inner `Sync`) | Shared | `tryAcquireShared` decrements available permits |
| **`CyclicBarrier`** | `AbstractQueuedSynchronizer` (inner `Sync`) | Shared | `tryAcquireShared` returns 0 when all parties arrive |

Each primitive overrides the template methods to define resource-specific logic while inheriting the robust queue management, cancellation handling, and interrupt response from AQS AbstractQueuedSynchronizer.

## Summary

- **AQS AbstractQueuedSynchronizer** provides the foundational framework for `java.util.concurrent` locks and synchronizers by managing a **CLH FIFO queue** and an **integer state** variable.
- Subclasses implement **`tryAcquire`/`tryRelease`** for exclusive resources or **`tryAcquireShared`/`tryReleaseShared`** for shared resources, leaving queue mechanics to the framework.
- **Exclusive mode** powers `ReentrantLock`, ensuring only one thread holds the lock at a time.
- **Shared mode** enables `CountDownLatch`, `Semaphore`, and `CyclicBarrier` to allow multiple concurrent accessors.
- The framework uses **parking/unparking** via `LockSupport` to block and wake threads efficiently without spin-waiting.

## Frequently Asked Questions

### What is the primary purpose of AQS AbstractQueuedSynchronizer?

AQS AbstractQueuedSynchronizer serves as a reusable synchronization framework that abstracts the complexity of blocking threads, managing wait queues, and performing atomic state updates. It allows developers to build custom locks and synchronizers by implementing only the resource-specific acquisition logic while inheriting robust queue management.

### How does AQS AbstractQueuedSynchronizer handle thread blocking?

When a thread fails to acquire the resource, AQS encapsulates it in a `Node` and appends it to the CLH queue tail using CAS operations. The thread is then **parked** using `LockSupport.park()`, which removes it from the CPU scheduler until another thread calls `release()` and **unparks** the queue head via `LockSupport.unpark()`.

### What distinguishes exclusive mode from shared mode in AQS?

Exclusive mode, used by `ReentrantLock`, allows only one thread to hold the resource, and subsequent acquirers are queued. Shared mode, used by `CountDownLatch` and `Semaphore`, permits multiple threads to acquire the resource simultaneously; `tryAcquireShared` returns a non-negative value to signal success, and AQS propagates the release signal to subsequent nodes if the resource remains available.

### Which Java classes extend AQS AbstractQueuedSynchronizer?

Core J.U.C classes including `ReentrantLock` (via inner class `Sync`), `ReentrantReadWriteLock`, `CountDownLatch`, `Semaphore`, and `CyclicBarrier` all extend AQS AbstractQueuedSynchronizer. Each implements the protected template methods to define custom synchronization policies while reusing the framework's queue infrastructure.