introduce
AbstractQueuedSynchronizer synchronizer is used to construct the basis of locks and other synchronous components framework and its implementation is mainly depends on an int member variables to represent the sync and through a FIFO queue waiting queue. Its subclasses must override several protected methods of AQS to change the synchronization state, while others implement queuing and blocking mechanisms. State updates use getState,setState, and compareAndSetState.
Subclasses recommended defined as custom synchronous components of a static inner class, synchronizer itself does not implement any synchronization interfaces, it is simply defines the number of sync acquisition and release methods for use by the custom of synchronous components, synchronizer supports both exclusive access to sync, can also support Shared access to sync, This makes it easy to implement different types of synchronous components.
Synchronizer is the key to implement lock (or any synchronization component). Synchronizers are aggregated in the implementation of lock, and the semantics of lock are realized using synchronizers. The relationship between the two can be understood as follows: lock is user-oriented, it defines the interface between the user and lock, and hides the implementation details; Synchronizer is a lock-oriented implementor, which simplifies the implementation of locks and shields the management of synchronization state, queuing, waiting and wake up of threads and other low-level operations. Locks and synchronizers nicely separate the areas of concern for consumers and implementers.
The above concept content reference link: juejin.cn/post/684490…
Source code analysis
Synchronous queue
When a shared resource is preempted by multiple threads, the thread that does not grab the lock will be blocked and enter the synchronization queue. The synchronous queue of AQS is a bidirectional linked list structure with head and tail Pointers.
private transient volatile Node head;
private transient volatile Node tail;
Copy the code
- Head and tail record the head and tail of the list
static final class Node { static final int CANCELLED = 1; Static final int SIGNAL = -1; Static final int CONDITION = -2; Static final int PROPAGATE = -3; // waitStatus shared lock biaoshi volatile int waitStatus; volatile Node prev; volatile Node next; volatile Thread thread; Node nextWaiter; }Copy the code
- The Node class points to front and back nodes
- WaitStatus is the wait state of the node. You can take a look at the waitStatus
The structure is shown as follows:
Get an exclusive lock
Below we look at the process of obtaining the exclusive lock according to the source code
public final void acquire(int arg) { if (! AcquireQueued (addWaiter(Node.exclusive), arg) selfInterrupt(); tryAcquire(arg) && }Copy the code
There are two methods for entering the sync queue, where addWaiter() encapsulates the current thread as a Node Node and adds it to the end of the sync queue;
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; }Copy the code
This code will not be explained in detail, relatively simple, you can follow my flow chart to understand
How does the acquireQueued() method implement lock acquisition for nodes in a synchronized queue
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; // Interrupt flag for (;;) { final Node p = node.predecessor(); If (p == head && tryAcquire(arg)) {setHead(node); if (p == head && tryAcquire(arg)) {setHead(node); p.next = null; // help GC failed = false; return interrupted; // Return whether the interrupt flag, Here is the only export} / / spin acquiring the synchronization status failed to enter the method the if (shouldParkAfterFailedAcquire (p, node) && parkAndCheckInterrupt ()) interrupted = true; } } finally { if (failed) cancelAcquire(node); }} / / judge whether to interrupt way after acquiring the synchronization status failed private static Boolean shouldParkAfterFailedAcquire (Node Mr Pred, Node node) { int ws = pred.waitStatus; If (ws == Node.SIGNAL) return true; if (ws == node. SIGNAL) return true; If (ws > 0) {// Do {node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; -0 compareAndSetWaitStatus(pred, ws, node.signal); // CAS changes the wait state of the front node to wake up} return false; } private final Boolean parkAndCheckInterrupt() {locksupport.park (this); Return thread.interrupted (); }Copy the code
The leftmost part of the figure should not return false, but rather the value of interrupted
See here to get an idea of the process:
- If the synchronization state fails, wrap the current thread as a Node and append it to the end of the list (addWaiter method)
- If the current node spins to determine whether its front node is the head node and the synchronization status is successfully obtained again, the current node is set to the value of the head node returning interrupted
- If failed to get the synchronization status again, according to the preceding nodes wait states to make corresponding processing, shouldParkAfterFailedAcquire
- SIGNAL, return true
- CANCELLED—- Look forward recursively for nodes whose waitStatus is not greater than 0 and set as the precursor of the current node, returning false
- Other —- sets waitStatus of the precursor node to SIGNAL (waitStatus of the newly created node is not specified as 0) and returns false
- If shouldParkAfterFailedAcquire returns true, then the interrupt processing was carried out on the current node (sync release will awaken to the interrupted status), or spin re-enter the second step of judgment
It can also be seen that after entering the synchronization queue, the node starts to spin to judge whether it has obtained the synchronization state or interrupts its own thread to wake up and enter the spin again to obtain the synchronization state when the front node releases the synchronization state.
Where is the unfairness of obtaining synchronization state?
Here you can think about, after entering the synchronous queue nature is a state of waiting in line, because need to awaken the rear front node, but for before into synchronous queue thread does not have the right of competition at the same time the lock, so the fairness is here, behind the fair lock can be found
Ok, continuing with the acquireQueued method that returns the identity of whether or not the node is broken when it acquires the synchronization status, we go back to our acquire method
public final void acquire(int arg) { if (! AcquireQueued (addWaiter(Node.exclusive), arg) selfInterrupt(); tryAcquire(arg) && }Copy the code
If acquireQueued returns false, the method ends and the thread acquires the lock for further business operations. If acquireQueued returns true, selfInterrupt() is executed. Setting the interrupt status of the current thread to true does not interrupt the thread. It may be useful elsewhere for interrupt handling, but I haven’t found it yet
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
Copy the code
Ok, so that’s the end of getting synchronized state.
Exclusive lock release
Also look at the release process according to the source code
Public final Boolean release(int arg) {if (tryRelease(arg)) {public final Boolean release(int arg) {tryRelease(arg); if (h ! = null && h.waitStatus ! = 0) unparkSuccessor(h); Return true; } return false; } // Private void unparksucceeded (Node Node) {int ws = node.waitstatus; if (ws < 0) compareAndSetWaitStatus(node, ws, 0); 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
The exclusive lock release code is relatively simple and will not be explained in detail.
This article is also written by the author according to his own understanding when reading the source code, if there are inaccurate places, trouble correction, thank you.