AQS class diagram structure

AQS full name is AbstractQueuedSynchronizer, namely abstract synchronous queue. See the structure of AQS class diagram below:

State property

A single shared state state, volatile modifier, is maintained in the AQS for synchronizer synchronization. The CAS algorithm based on optimistic locking is used in its update.

CLH queue

CLH(Craig, Landin, and Hagersten LOCKS) synchronization queues are FIFO bidirectional queues that internally record the first and last queue elements through the head and tail nodes. The queue elements are of type Node. AQS rely on it to complete the synchronization state of the state of management, the current thread if acquiring the synchronization state failure, AQS will wait for the current thread already wait state information structure into a Node (the Node) and add it to the CLH synchronous queue, blocks the current thread at the same time, when the sync release, will wake the first Node (fair), Make it try again to get the synchronization status.

The Node Node

In CLH synchronization queue, a node represents a thread, which stores thread reference (Thread), state (waitStatus), precursor node (PREv) and successor node (Next). The subsequent node (nextWaiter) of condition queue is shown as follows:

WaitStatus Indicates the following status:

loaded

CLH queue enqueueing is tail pointing to the new node, the prev of the new node pointing to the last node, and the next of the last node pointing to the current node. The addWaiter method is as follows:

Private Node addWaiter(Node mode) {Node Node = new Node(thread.currentThread (), mode); // Try the fast path of enq; backup to full enq on failure Node pred = tail; if (pred ! = null) { node.prev = pred; If (compareAndSetTail(pred, node)) {pred.next = node; return node; }} // Multiple attempts to enq(node); return node; }Copy the code

If addWaiter fails to set the tail Node, call the enq(Node Node) method to set the tail Node as follows:

Private Node enq(final Node Node) {// Loop for (;;) { Node t = tail; If (t == null) {// Must initialize if (compareAndSetHead(new Node())) tail = head; } else { node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; }}}}Copy the code

Break the ranks

When the thread of the primary node releases the synchronization state, it wakes up its successor node (next), which sets itself up as the primary node when the synchronization state is successfully acquired. You can see the following source code:

private void unparkSuccessor(Node node) { /* * If status is negative (i.e., possibly needing signal) try * to clear in anticipation of signalling. It is OK if this * fails or if status is changed by waiting thread. */ int ws = node.waitStatus; if (ws < 0) compareAndSetWaitStatus(node, ws, 0); /* * Thread to unpark is held in successor, which is normally * just the next node. But if cancelled or apparently null, * traverse backwards from tail to find the actual * non-cancelled successor. */ 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); }Copy the code

Acquire method flow is as follows:

Exclusive and share mode

AQS supports two synchronization modes: exclusive and shared.

exclusive

Only one thread holds synchronization state at a time, such as ReentrantLock. Can divide for fair lock and non – fair lock again.

  • Fair lock: First come first to get the lock according to the order the threads are queued in the queue.
  • Unfair lock: when a thread wants to acquire a lock, it directly grabs the lock regardless of the queue order, which is unreasonable.

Shared

Multiple threads can execute simultaneously, such as Semaphore/CountDownLatch are shared products.

reference

Juejin. Cn/post / 684490…