Talk about high concurrency (24) parsing java.util.concurrent each component (vi) in-depth understanding Aqs (iv)

Source: Internet
Author: User
Tags cas prev

Recently, the overall structure of the next aqs is over. Also on the Internet to read some of the articles about Aqs, most of the articles are generalities. Once again, I looked at the code of Aqs and took some new points out to say.


AQS is a tube process. Provides a primary Synchronizer's ability to include a state that changes the state of an atomic operation. and a sequence of actions for the synchronization thread. It is a variant of Clhlock, and Clhlock is a spin lock algorithm based on queue lock.

The AQS also uses queues as the structure of the synchronization thread. It maintains a queue of two. One is a synchronization queue that is synchronized as a thread, and a conditional queue that is based on unsafe for blocking/waking operations.

So understanding queue operations is the key to understanding Aqs.

1. Understanding Head, tail reference

2. Understand next, prev quote

3. Understand when queue nodes are queued and when to team up


What you need to remember about the head reference

1. The head reference always points to the node that acquired the lock and it is not canceled .

Acquire operation success means to obtain the lock, acquire the assumption of interruption, then the acquire failed, when the head will refer to a node down.

* Because the head node is never cancelled:a node becomes         * head only as A result of successful acquire. A         * cancelled thread never succeeds in acquiring, and a thread is only         * cancels itself, not any other node.

When a lock is obtained, assuming the thread is interrupted, release is required to free the head node.

Assuming a thread breaks the lock without releasing it, it can cause problems. So when an explicit lock is used. Must release the lock in finally

Lock lock = new Reentrantlock (); Lock.lock () try{//assumed interrupt, able to handle get thrown, to ensure release of lock}finally{lock.unlock in finally ();}

Take a look at the processing of the head reference when the lock is acquired, only if the node's precursor node is head, it is possible to acquire the lock, and after the lock is acquired, it is set to the head node, and the old head's next is set to null at the same time.

Here are a few layers of meaning:

1. Always start getting locks from the head node

2. After the new thread obtains the lock, the node that previously acquired the lock is out of the queue

3. Once the lock is acquired, the acquire method must return, and the process will not be interrupted

Final Boolean acquirequeued (final node node, int arg) {        Boolean failed = true;        try {            Boolean interrupted = false;            for (;;) {                final Node p = node.predecessor ();                if (p = = head && tryacquire (ARG)) {                    Sethead (node);                    P.next = null; Help GC                    failed = false;                    return interrupted;                }                if (Shouldparkafterfailedacquire (p, node) &&                    parkandcheckinterrupt ())                    interrupted = true;            }        } Finally {            if (failed)                cancelacquire (node);        }    }

About tail references. It is responsible for implementing a chain-free structure without locking. The use of CAs + polling method.

The queued operation of the node is at the tail node

Private node Enq (final node node) {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;}}}    


The next reference plays a very critical data in the queue. It appears in very high frequency. About next reference. It has several values in the case

1. Next = null

2. Next points to the next non-null node

3. Next = node itself


Next = null case there are three kinds of

1. The end of the queue node, the next of the tail node is not explicitly set. So is null

2. The next node of the last queue node when the tail node is queued is potentially null because ENQ is not an atomic operation, and CAS is a composite operation

Private node Enq (final node node) {for        (;;) {            Node t = tail;            if (t = = null) {//Must initialize                if (Compareandsethead (new Node ()))                    tail = head;            } else {                Node.prev = t ;                if (Compareandsettail (t, node)) {                    //This period next may be null                   t.next = node;                    return t;}}}    

3. When a lock is acquired, the next of the node that acquired the lock is set to NULL

if (p = = head && tryacquire (ARG)) {                    Sethead (node);                    P.next = null; Help GC                    failed = false;                    return interrupted;                }

next points to the non-null next node , which is the normal waiting node in the synchronization queue, which sets the next value of the previous node when the lock is released, to notify the next node to acquire the lock

private void Unparksuccessor (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);    }

Next point to yourself, this is the cancel operation, will point the node's previous node to its next node, and finally set the next field to its own

private void Cancelacquire (node node) {       //Ignore if Node doesn ' t exist & nbsp;      if (node = = null)              return;        Node.thread = null;        /Skip cancelled predecessors        Node pred = node.prev;         while (Pred.waitstatus > 0)              Node.prev = pred = pred.prev;       //Prednext is the apparent no De to Unsplice. CASes below will       //fail if not, in which case, we lost race vs another cancel&n bsp;      //or signal, so no further action is necessary.         Node Prednext = pred.next;       //Can use unconditional write instead of CAS here.        /Atomic step, other Nodes can skip past us.       //before, we AR e free of interference from other threads.        Node.waitstatus = Node.CANCELLED;& nbsp;      //If We are the tail, remove ourselves.         if (node = = Tail && compareandsettail (node, pred)) {             Compareandsetnext (pred, prednext, null);       } else {            //IF successor needs signal, try to set pred ' s next-link& nbsp;          //So it'll get one. Otherwise wake it up to propagate.            int ws;            if (pred! = Head &&                 (ws = Pred.waitstatus ) = = Node.signal | |                  (ws <= 0 & & Compareandsetwaitstatus (pred, WS, node.signal)) &&                 Pred.thread! = null) {                 Node next = node.next;                 if (next! = null && next.waitstatus <= 0)                      Compareandsetnext (pred, Prednext, next); &NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&Nbsp;  } else {                 Unparksuccessor (node);           }             node.next = node; Help gc       }   }

Prev Reference is relatively simple, it is mainly to maintain the structure of the chain list. Clhlock is the state spin in the previous node, the nodes inside the Aqs are not waiting in the previous state, but are released when the previous node notifies the queue to find the next node to be awakened.


Finally, the node enters the queue and out of the queue situation.


there is only one case where the node is in the queue. That is, its tryacquire operation failed, did not obtain the lock, entered the synchronization queue wait, assuming tryacquire success, there is no need to enter the synchronization queue waiting . The AQS provides full flexibility. It provides the Tryacquire and Tryrelase methods to subclass extensions. The base class is responsible for maintaining queue operations, and subclasses can decide for themselves whether or not to enter the queue.

So there are two types of actual subclass extensions, one is a fair synchronizer, and the other is a non-fair synchronizer. It is important to note that the so-called unfairness does not mean that the queue is not used to maintain the blocking operation, but rather that the first-coming thread is not considered when acquiring the competition, and later threads can compete directly for resources. The non-fair and fair Synchronizer competition fails, it is necessary to enter the Aqs synchronization queue to wait, and the synchronization queue is first to serve the fair queue.

Static final class Nonfairsync extends Sync {        private static final long serialversionuid = -2694183684443567898l;        Nonfairsync (int permits) {            super (permits);        }        protected int tryacquireshared (int acquires) {            return nonfairtryacquireshared (acquires);        }    }    /**     * Fair version */    static final class Fairsync extends Sync {        private static final long Serialversio nuID = 2014338818796000944L;        Fairsync (int permits) {            super (permits);        }        protected int tryacquireshared (int acquires) {for            (;;) {                if (hasqueuedpredecessors ())                    return-1;                int available = GetState ();                int remaining = Available-acquires;                if (Remaining < 0 | |                    Compareandsetstate (available, remaining))                    return remaining;}}    

There are two scenarios for out-of-queue.

1. After a thread gets the lock is. The head reference points to the thread that is currently acquiring the lock. The previous node that acquired the lock itself actively out of the queue

2. When the operation is canceled. Node out of the queue, cancel only two cases, one is the thread is interrupted, the other is waiting for timeout


Talk about high concurrency (24) parsing java.util.concurrent each component (vi) in-depth understanding Aqs (iv)

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.