Java Multithreading--Juc packet Source Analysis 9--Abstractqueuedsynchronizer deep analysis--semaphore and Countdownlatch__aqs

Source: Internet
Author: User
Tags cas semaphore

In the previous analysis of the Reentrantlock/reentrantreadwritelock, we have analyzed the Aqs. After a preliminary understanding of Aqs, this article attempts to make a more systematic analysis of it. Because Aqs is the cornerstone of the entire synchronization framework, not just locks, but also many other sync components, such as semaphore, Countdownlatch, are built on Aqs.
-The cornerstone of the aqs– synchronization framework
-3 Core technical principles of AQS
-Aqs Source Analysis
Aqs Exclusive mode and shared mode
The cornerstone of the semaphore and Countdownlatch aqs-synchronization Framework

The following figure shows the classes that inherit from Aqs in the entire JUC package. As you can see, Reentrantlock/reentrantreadwritelock, Semaphone, Countdownlatch, Futuretask and other components are built on Aqs.
3 Core technical principles of AQS

(1) An int type atom variable state. All threads have CAS access to this variable to determine whether they should block or enter a critical section.
(2) A pair of Park/unpark primitives that enable the precise control of 1 threads over another 1 threads: blocking, awakening
(3) With no linked list of all the blocked threads, formed a blocking queue

The basic ideas are as follows:
When acquire, the judge state,state condition satisfies, enters the critical section executes the code, does not satisfy, joins itself to block the queue, then blocks oneself;
When release, update state, while awakening the successor in the queue, after the successor awakened, again acquire take the lock, walk acquire process.

The following is the AQS source code inside the key function analysis; Aqs Source resolution Aqs interface Layer

Aqs's external interface mainly includes the following:
Exclusive mode
Acquire
acquireinterruptibly
Release

Shared model
Acquireshared
acquiresharedinterruptibly
Releaseshared

    Public final void acquire (int arg) {if!tryacquire (ARG) &&//Template method acquirequeued (Addwai
    ter (node.exclusive), Arg)) Selfinterrupt ();
            Public final void acquireinterruptibly (int arg) throws Interruptedexception {if (thread.interrupted ())
        throw new Interruptedexception ();
    if (!tryacquire (ARG))//Template Method doacquireinterruptibly (ARG);
            Public final Boolean release (int arg) {if (Tryrelease (ARG)) {//template method Node h = head;
            if (h!= null && h.waitstatus!= 0) unparksuccessor (h);
        return true;
    return false; Public final void acquireshared (int arg) {if (tryacquireshared (ARG) < 0)//template Method Doacqu
    Ireshared (ARG); Public final void acquiresharedinterruptibly (int arg) throws Interruptedexception {if (thread.interrupted () ) throw new InterrupTedexception ();
    if (tryacquireshared (ARG) < 0)//Template Method doacquiresharedinterruptibly (ARG); Public final Boolean releaseshared (int arg) {if (tryreleaseshared (ARG)) {//Template method doreleases
            Hared ();
        return true;
    return false; }

In the above interface, there are 4 template methods left for subclasses to implement, that is, the various sync in the class diagram:
Tryacquire
Tryrelease
Tryacquireshared
several core functions of tryreleaseshared Aqs

From the above code, you can see the implementation details inside the AQS, mainly several functions:

3 key functions in exclusive mode:
private void acquirequeued ()//shield interrupt, enter queue
private void doacquireinterruptibly (int arg)//Response Interrupt
private void Unparksuccessor (node node)//minus, awakening successor

2 key functions in shared mode:
private void doacquiresharedinterruptibly (int arg)//Plus
private void doreleaseshared () {//minus

The following analysis: Exclusive mode core code Analysis

    Acquirequeued (Addwaiter (node.exclusive), Arg));  Addwaiter, into the queue final Boolean acquirequeued (final node node, int arg) {try {Boolean interrupted
            = false; for (;;)
                {final Node P = node.predecessor ();   if (p = = head && tryacquire (ARG)) {//to fetch the lock Sethead (node);   
                    Out pairs of columns p.next = NULL;
                return interrupted; } if (Shouldparkafterfailedacquire (p, node) && parkandcheckinterrupt ())//Blocking   oneself interrupted = true; Awakened from obstruction, back to the top.
            Continue dead Loop} catch (RuntimeException ex) {Cancelacquire (node);
        Throw ex; } private void doacquireinterruptibly (int arg) throws Interruptedexception {final node node =   Addwaiter (node.exclusive); Into queue try {for (;;) {final Node p = node.pRedecessor ();  if (p = = head && tryacquire (ARG)) {//to fetch the lock Sethead (node);  
                    Out pairs of columns p.next = NULL;
                Return } if (Shouldparkafterfailedacquire (p, node) && parkandcheckinterrupt ())//Blocking                     their own break;
            Interrupted wakeup, direct exit} catch (RuntimeException ex) {Cancelacquire (node);
        Throw ex;  }//Arrive Here is only if interrupted cancelacquire (node);
    Was interrupted to wake up, did not get the lock exited, cancels Accquire throw new interruptedexception ();
        } private void Unparksuccessor (node node) {//wake successor 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); }

Here are 2 key points:
(1) acquirequeued shielding interrupt, doacquireinterruptibly response interrupted. The only difference is that the former is interrupted to wake up, continue to die loop to hold the lock until it is received;
The latter is interrupted to wake up, and the direct break jumps out of the For Dead loop.
(2) Whether in the queue, or out of the queue, are in the acquire time, occurred. Release, only responsible for awakening the successor. So the logic of release is quite simple, the key acquire more complex, the overall idea is as follows:

Step1:tryacquire, success, then directly execute the code behind. Failed, didn't get the lock.
Step2: Call Addwaiter to add itself to the blocking queue (note that there is no blocking at this time.) )
Step3: Start for Loop, try again to tryacquire once, if get, out queue;
Step4: Don't get it, block yourself
Step5: Interrupted or awakened by Unparksuccessor, after awakening;
If you do not respond to interrupts, go back to step 3 and try to hold the lock again; So dead loops, until you get the lock, the function returns.
If the response is interrupted, the lock is returned and the acquire is canceled before it is returned, i.e. Cancelacquire;

The above is the Code analysis for exclusive mode. Before continuing to parse shared-mode code, there is a clear concept of "exclusive mode" and "Shared mode":

# Exclusive mode and shared mode
Exclusive mode: When the acquire, only 1 threads can get the resources, release, 1 times can only wake 1 threads. Reentrantlock, Reentrantreadwritelock.writelock, are all used the exclusive mode of AQS;

Shared mode: When acquire, you can have multiple threads to get resources at the same time, when release, all threads in the queue are awakened, that is, propagate, and then the lock is also taken.
Reentrantreadwritelock.readlock, Semaphore, Countdownlatch are all shared patterns used.
For example, for Semaphore: acquire, as long as the state > 0, you can get the lock;
Release, it is not necessarily free only 1, can be released (n), means that the same time the release of n Resources, All threads in the blocking queue are awakened, and then there are n threads that can get the lock at the same time;
For example, for Countdownlatch, when release to Countdown = 0, all threads waiting for countdown = 0 are awakened and both are locked. shared mode Code Analysis

private void doacquiresharedinterruptibly (int arg) throws Interruptedexception {final node node = addwaiter node.   SHARED); Into queue try {for (;;)
            {final Node P = node.predecessor ();   if (p = = head) {int r = tryacquireshared (ARG);  To fetch the lock if (r >= 0) {setheadandpropagate (node, r); Take the lock, get out of the team, and wake the successor P.next = NULL;
                Help GC return;   } if (Shouldparkafterfailedacquire (p, node) && parkandcheckinterrupt ())                     Block oneself break;
        Interrupted wakeup} catch (RuntimeException ex) {Cancelacquire (node);
    Throw ex;                }//Arrive Here is only if interrupted cancelacquire (node);
Be interrupted to wake, do not get lock, exit throw new Interruptedexception (); } private void Setheadandpropagate (node node, int propagate) {node H = head; Head for check below Sethead (node);
        if (Propagate > 0 | | | h = NULL | | H.waitstatus < 0) {Node s = node.next;   if (s = = NULL | | s.isshared ()) doreleaseshared (); Key point}} private void Doreleaseshared () {for (;;)
        {Node h = head;
            if (h!= null && H!= tail) {int ws = H.waitstatus;  if (ws = = node.signal) {if (!compareandsetwaitstatus (H, node.signal, 0)) continue;//   Loop to Recheck cases unparksuccessor (h); Wakeup successor} else if (ws = = 0 &&!compareandsetwaitstatus (H, 0, Node.pro Pagate)) continue;
    Loop on failed CAS} if (h = = head)//loop if head changed break;
 }
}

Here's a key point: Doreleaseshared wakes up all the threads in the waiting queue, and then the thread again goes for the lock. For Countdownlatch, all threads scramble to Countdown = 0, so all threads get a lock, and for Semaphore.release (1), only 1 resources are freed, but all threads are awakened, and then only 1 threads can get the resource, The remaining n-1 thread, again into the blocking state.

#semaphore and Countdownlatch
Figuring out the AQS shared-mode principle, semaphore and countdownlatch are simple. 2 is just the opposite of the judgment of the state variable, which can be said to be just the reverse:
Semaphore:state > 0, entering; state = 0 blocking.
Countdownlatch:state > 0, blocking; state = 0, entering;

The following comparison of the 2 of the tryacquireshared will know:

Countdownlatch.sync protected int tryacquireshared (int acquires) {return (getState () = 0)? 1:-1   ;
            state = 0, returns true}//semaphore.nonfairsync final int nonfairtryacquireshared (int acquires) { for (;;)
                {int available = GetState ();
                int remaining = Available-acquires;   if (Remaining < 0 | |
                    Just the opposite of Countdownlatch, state >= 0 returns True Compareandsetstate (available, remaining))
            return remaining; }//semaphore.fairsync protected int tryacquireshared (int acquires) {for (;;)
                {if (hasqueuedpredecessors ()) return-1;
                int available = GetState ();
                int remaining = Available-acquires;    if (Remaining < 0 | |
       Contrary to Countdownlatch, state > = 0 returns ture compareandsetstate (available, remaining))             return remaining; }
        }

At the same time, tryrelaseshared is just the opposite, one is + +, one is-

Countdownlatch.sync
        protected Boolean tryreleaseshared (int releases) {
            //decrement count; signal when Transition to zero for
            (;;) {
                int c = getState ();
                if (c = = 0) return
                    false;
                int NEXTC = c-1;     Resource-
                if (Compareandsetstate (c, NEXTC)) return
                    NEXTC = = 0;
            }
        }

Semaphore.sync
        Protected Final boolean tryreleaseshared (int releases) {for
            (;;) {
                int current = GetState ();
                int next = current + releases;   Resource + +
                if (Next < current)//overflow
                    throw new Error ("Maximum Permit count Exceeded");
                if (Compareandsetstate (next)) return
                    true;
            }
        

Summary:
The biggest difference between shared and exclusive mode is that in shared mode, a release wakes up all the threads in the queue, even if this releases only frees one resource, and only one thread can get the resource. Once all threads wake up and scramble for resources again, all threads may be competing (for example, Countdownlatch), or there may be only one thread competing (for example, Semaphore.release (1)).

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.