Learn AQS
A Dian AQS
What is the AQS? Java and contracting in the abstract queue synchronizer (AbstractQueuedSchronizer), which is the basic framework in java build locks and other synchronization components such as common reentrant lock ReetrantLock, are based on the framework to achieve synchronization.
Two Dian understand
The whole idea, to maintain a lock status State (int type), and a first in first out (FIFO) queue.
When the lock to compete, CAS will use multiple threads to modify the operating state value, if the modification is successful (success to obtain a lock), then the thread identifier for the thread to acquire the lock, if the modification fails, the thread into the end of the queue, and be blocked until the thread is awakened when the head of the queue node thread (i.e. thread acquired the lock) releases the lock, the thread will be awakened descendant node.
Wed and parsing source code
3.1) to obtain a lock
ReentrantLock the source inlet lock () method, in the case of using a non-fair lock, call lock () method is an internal class NonFairSync.
Final void Lock () { IF (compareAndSetState (0,. 1 )) // first acquire the lock state is provided using cas setExclusiveOwnerThread (Thread.currentThread ()); // If the acquisition is successful, identifying the thread acquires the lock for the current thread the else Acquire ( 1 ); // try to acquire the lock again, and if that fails will be added to the waiting queue }
The AQS acquire () method:
public Final void Acquire ( int Arg) { IF (! to tryAcquire (Arg) && // Subclasses implement to tryAcquire () method, such as fair ReentrantLock the lock, the lock unfair achieve acquireQueued (addWaiter (Node.EXCLUSIVE), arg )) // add the current thread into the wait queue, if the successor node is the first node, and then attempt to acquire the lock again selfInterrupt (); // self interrupt }
addWaiter AQS in () method
/** * Creates and enqueues node for current thread and given mode. * * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared * @return the new node */ 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)) {// first attempt Direct team pred.next = Node; return Node; } } ENQ (Node); // queue into the return Node; }
enq AQS in () method
Private the Node ENQ ( Final the Node the Node) { for (;;) { // infinite loop, it uses cas been trying until it succeeds position the Node t = tail; IF (t == null ) { // Must the initialize IF (compareAndSetHead ( new new the Node ())) tail = head; } the else { node.prev = T; IF (compareAndSetTail (T, Node)) { t.next = Node; return t; } } } }
acquireQueued AQS in () method
/** * Acquires in exclusive uninterruptible mode for thread already in * queue. Used by condition wait methods as well as acquire. * * @param node the node * @param arg the acquire argument * @return {@code true} if interrupted while waiting */ final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; For (;;) {// infinite loop Final the Node P = node.predecessor (); IF (P == head to tryAcquire && (Arg)) { // if the successor node is the head node of the current node, then try to obtain lock setHead (Node); p.next = null ; // Help the GC failed = to false ; return interrupted; } IF (shouldParkAfterFailedAcquire (P, Node) && parkAndCheckInterrupt ()) // blocks the current thread interrupted = to true; } } finally { if (failed) cancelAcquire(node); } }
AQS in shouldParkAfterFailedAcquire () method for determining whether should block
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; if (ws == Node.SIGNAL) /* * This node has already set status asking a release * to signal it, so it can safely park. */ return true; // condition.signal() , 请求释放锁, 说明可以安全阻塞 if (ws > 0) { /* * Predecessor was cancelled. Skip over predecessors and * indicate retry. */ do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { /* * waitStatus must be 0 or PROPAGATE. Indicate that we * need a signal, but don't park yet. Caller will need to * retry to make sure it cannot acquire before parking. */ compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; }
3.2) releases the lock
Source of inlet ReentrantLock unlock () method, the AQS actually call release () method
/** * Releases in exclusive mode. Implemented by unblocking one or * more threads if {@link #tryRelease} returns true. * This method can be used to implement method {@link Lock#unlock}. * * @param arg the release argument. This value is conveyed to * {@link #tryRelease} but is otherwise uninterpreted and * can represent anything you like. * @return the value returned from {@link #tryRelease} */ public final boolean release(intarg) {// used in the exclusive mode release IF (tryRelease (Arg)) {// tryRelease () method is implemented by a subclass the Node H = head; IF ! (H = null ! = 0 && h.waitStatus ) unparkSuccessor ( h); // wake successor nodes return to true ; } return to false ; }
In shared mode, a corresponding method of releasing acquireShared ()
/** * Acquires in shared mode, ignoring interrupts. Implemented by * first invoking at least once {@link #tryAcquireShared}, * returning on success. Otherwise the thread is queued, possibly * repeatedly blocking and unblocking, invoking {@link * #tryAcquireShared} until success. * * @param arg the acquire argument. This value is conveyed to * {@link #tryAcquireShared} but is otherwise uninterpreted * and can represent anything you like. */ public final void acquireShared(int arg) { if (tryAcquireShared(arg) < 0) doAcquireShared(arg); }
unparkSuccessor AQS in () method
/** * Wakes up node's successor, if one exists. * * @param node the node */ 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); }
3.3) several wait state
/ ** waitStatus value to Indicate the Thread has Canceled * / static Final int CANCELED = 1; // cancel / ** waitStatus value Indicate by successor to the Thread's Needs unparking * / static Final int the SIGNAL = -1; // pointed successor node thread needs wake / ** waitStatus value to indicate the thread iS oN waiting for condition condition * / static Final int cONDITION = -2; // pointed out that the thread is waiting for the appropriate conditions / ** * indicate at the waitStatus value to the Next acquireShared Should * UNCONDITIONALLY the Propagate * / static Final int PROPAGATE = -3; // indicates the next acquire a shared lock requires unconditional propagation
3.4) two ways of getting lock mode
/** Marker to indicate a node is waiting in shared mode */ static final Node SHARED = new Node(); // 共享 /** Marker to indicate a node is waiting in exclusive mode */ static final Node EXCLUSIVE = null; // 排他
Learning materials:
< On the java concurrent series >
< Java concurrent Explanation of AQS >