Master Java's AbstractQueuedSynchronizer: Concepts, Mechanics, and Custom Implementations
This article explains the fundamentals of Java's AbstractQueuedSynchronizer (AQS), covering its basic concepts, core components, lock and unlock mechanisms, condition objects, and provides a pseudo‑code example for building a custom AQS implementation.
AQS Basic Concepts and Functions
AQS stands for AbstractQueuedSynchronizer, a framework for building synchronizers.
Shared mode (shared lock/read lock) allows multiple threads to acquire the lock for reading.
Exclusive mode (exclusive lock/write lock) allows only one thread to hold the lock for writing.
Both modes share the same FIFO wait queue.
The state field holds synchronization state and is volatile with CAS for visibility and atomicity.
Main Functions of AQS
Provides a template framework for blocking locks and related synchronizers based on a FIFO wait queue.
Subclasses must define a non‑public helper class extending AQS and use its protected methods to implement lock behavior.
Subclasses rely on CAS operations on the state field and volatile semantics to maintain atomicity and visibility.
ReadWriteLock typically implements both shared and exclusive modes.
AQS defines an internal ConditionObject class that works with exclusive mode to enable thread communication via await/signal.
AQS Core Components
Key methods for using AQS subclasses: tryAcquire, tryRelease, tryAcquireShared, tryReleaseShared, isHeldExclusively, etc. Node inner class implements a double‑ended linked list for the wait queue, with fields prev, next, waitStatus, nextWaiter, thread, and mode.
static final class Node {
static final Node SHARED = new Node(); // shared lock
static final Node EXCLUSIVE = null; // exclusive lock
static final int CANCELLED = 1; // cancelled
static final int SIGNAL = -1; // signal
static final int CONDITION = -2; // condition
static final int PROPAGATE = -3; // propagate
volatile int waitStatus;
volatile Node prev;
volatile Node next;
volatile Thread thread;
Node nextWaiter;
Node(Thread thread, Node mode) {
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) {
this.waitStatus = waitStatus;
this.thread = thread;
}
}ConditionObject
Implements java.util.concurrent.locks.Condition.
Maintains firstWaiter and lastWaiter for the condition queue.
Provides await, signal, signalAll and internal methods for managing waiters.
public class ConditionObject implements Condition, Serializable {
private transient Node firstWaiter;
private transient Node lastWaiter;
private static final int REINTERRUPT = 1;
private static final int THROW_IE = -1;
// await, signal, signalAll implementations ...
private Node addConditionWaiter() { /* ... */ }
private void unlinkCancelledWaiters() { /* ... */ }
private void doSignalAll(Node first) { /* ... */ }
}AQS Core Attributes
state– synchronization state; its meaning varies per subclass. exclusiveOwnerThread – thread holding the exclusive lock. Unsafe – low‑level CAS operations. LockSupport – utilities for parking and unparking threads.
Working Principle of AQS
Core properties: state, exclusiveOwnerThread, and a double‑ended wait queue (head & tail).
Exclusive Lock Interface
Template methods: acquire / release.
Implementation methods: tryAcquire / tryRelease.
Shared Lock Interface
Template methods: acquireShared / releaseShared.
Implementation methods: tryAcquireShared / tryReleaseShared.
Lock Acquisition
acquire/ acquireShared define contention logic; if acquisition fails, the thread joins the wait queue. tryAcquire / tryAcquireShared perform the actual lock operation delegated to the concrete AQS subclass.
Lock Release
release/ releaseShared define release logic and wake up the next waiting node. tryRelease / tryReleaseShared perform the actual unlock operation in the subclass.
Summary of AQS Lock/Unlock Process
Locking and unlocking rely on the waitStatus field; PROPAGATE indicates that the lock has been released and other threads may contend.
The double‑ended queue is manipulated via CAS to safely update head and tail.
Shared mode uses PROPAGATE to notify waiting shared nodes after release.
Locking can be shortcut by the subclass’s specific implementation.
Lock acquisition adds the node to the queue, CAS spins to acquire, then removes the node; unlocking wakes the next node.
Custom AQS
Key elements for a custom AQS implementation:
Thread‑safe double‑ended blocking queue.
Thread‑safe exclusive owner thread.
Thread‑safe state attribute.
Pseudo‑code for a custom AQS implementation:
// DefineAQS.java
public abstract class DefineAQS {
final static class AQSNode {
static final int SHARED = 9999;
static final int EXCLUSIVE = -9999;
private int mode;
private volatile Thread thread;
public AQSNode(int mode) {
this.thread = Thread.currentThread();
this.mode = mode;
}
public Thread getThread() { return thread; }
public int getMode() { return mode; }
}
private AtomicInteger state = null;
private AtomicReference<Thread> exclusiveOwnerThread = new AtomicReference<>();
private LinkedBlockingQueue<AQSNode> waiters = new LinkedBlockingQueue<>();
public AtomicInteger getState() { return state; }
public void setState(int s) { this.state = new AtomicInteger(s); }
public void compareAndSetState(int expect, int update) { this.state.compareAndSet(expect, update); }
public void acquire(int arg) {
AQSNode node = new AQSNode(AQSNode.EXCLUSIVE);
waiters.offer(node);
while (!tryAcquire(arg)) {
LockSupport.park(node.getThread());
}
waiters.remove(node);
}
public void release(int arg) {
if (tryRelease(arg)) {
while (true) {
AQSNode node = waiters.peek();
if (node.getMode() == AQSNode.EXCLUSIVE) {
LockSupport.unpark(node.getThread());
break;
}
}
}
}
public void acquireShared(int arg) {
AQSNode node = new AQSNode(AQSNode.SHARED);
waiters.offer(node);
while (tryAcquireShared(arg) < 0) {
LockSupport.park(node.getThread());
}
waiters.remove(node);
}
public void releaseShared(int arg) {
if (tryReleaseShared(arg) > 0) {
while (true) {
AQSNode node = waiters.peek();
if (node.getMode() == AQSNode.SHARED) {
LockSupport.unpark(node.getThread());
break;
}
}
}
}
// abstract methods: tryAcquire, tryRelease, tryAcquireShared, tryReleaseShared
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Xiaokun's Architecture Exploration Notes
10 years of backend architecture design | AI engineering infrastructure, storage architecture design, and performance optimization | Former senior developer at NetEase, Douyu, Inke, etc.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
