Java Concurrency Aqs Synchronizer learning

Source: Internet
Author: User
Tags cas prev volatile

Aqs Queue Synchronizer Learning

In learning concurrency, we will be exposed to the tools in the JUC, JUC for us to prepare a lot of things in the concurrency need to use, but they are based on AQS(AbstractQueuedSynchronizer)队列同步器 to achieve, that is, if we can comb clear aqs among the knowledge points, It is very helpful for us to learn about other concurrency function keys later.

CLH Queue

Queue Synchronizer (Abstractqueuedsynchronizer), which is the underlying framework used to build locks or other synchronization components, uses an int variable to represent the synchronization state, completes the queue of resource acquisition threads through the built-in FIFO queue, and the author of the contract is Doug Lea expects her to be the basis for most of the synchronization needs.

This built-in queue is where the CLH双向队列 current thread, if it fails to acquire a lock, encapsulates information such as the current thread, state, and so on in a node node to be added to the CLH queue-that is, a node is actually a thread, and when the Wired path is released, Wakes up the CLH queue and fetches it from its first node:

  Static Final class Node {       /** Marker to indicate a Node was waiting in shared mode */&nbs   P   Shared mode node        static final node shared = new node ();        /** Marker to indicate a node was waiting in exclusive mode */ //Exclusive mode node   &NBS P     static final Node EXCLUSIVE = null;   ?        /** waitstatus value to indicate thread have cancelled */ //In Canceled wait status  /* for  A timeout or interrupt is in that state, and the node in that state is not turned into a different state         The node in that state will not be blocked again */       static final   int CANCELLED =  1;        /** waitstatus value to indicate successor ' s thread needs unparking *//wait status &nbs   P   /* Indicates if the successor needs to be awakened */       static final int SIGNAL    =-1;        /** waitstatus value to indicate thread was waiting on condition/ /* The node is in the conditional queue, whichThe node is not used as a synchronization queue until it is set to 0 for transmission and is moved to the synchronization queue, and the acquisition of synchronization status is added */       static final int CONDITION =-2;          /**         * Waitstatus value to indicate the next acquireshared should         * Unconditionally propagate         */ /* indicates that the next shared Sync State acquisition will have no  Conditional propagation Go down */       static final int PROPAGATE =-3;   ?   //thread wait status        volatile int waitstatus;   ?       //Front node of current node        volatile node prev;   ?         //nodes of the current node        volatile node next;   ?         //node's thread        volatile thread thread;   ?       //conditional queue the next waiting node        node Nextwaiter;   ?         /**         * Determine if the node is sharing mode         */    &nbsp    final Boolean isShared () {           return nextwaiter = = GKFX;        }?        /**         * Get front node         *     &NB Sp   final Node predecessor () throws NullPointerException {           node p = prev; Get front node            if (p = = null)//empty null pointer exception           &NBSP ;      throw new NullPointerException ();              else                return p;        }?          node () {   //used to establish initial head or SHARED marker         }  ?        node (thread thread, Node mode) {   //used by Addwaiter       &NBS P      this.nextwaiter = mode;           this.thread = thread;        }?         node (thread thread, int waitstatus) {//used by Condition              this.waitstatus = Waitstatus;              this.thread = thread;      }  }

Through the above on the node nodes of the source code into the commentary, I think for the subsequent content will be very helpful, because the following methods will have a lot of state judgment.

When we rewrite the Synchronizer, we need to use the Synchronizer's 3 methods to access and modify the state of the synchronization. respectively:

    • GetState (): Gets the current synchronization status

    • setState (int newstate): Sets the current synchronization state

    • Compareandsetstate (int expect, int update): Sets the current state through CAs, which guarantees the atomicity of the set state operation

Into row

Since we have already talked about the AQS in the maintenance of is CLH双向队列 , and is FIFO, since it is the queue, that there must be into row and the operation of the dequeue, we come first from the into row look:

Acquire (int arg) method

The method is the exclusive mode of the thread to get the synchronization state of the entry, if the current threads to obtain the synchronization state is successful, then returned by the method, if the acquisition is unsuccessful will go into the CLH queue to wait.

In this method, the overridden tryacquire (int arg) method is called.

  public final void acquire(int arg) {      if (!tryAcquire(arg) &&          acquireQueued(addWaiter(Node.EXCLUSIVE), arg))          selfInterrupt();  }
    • Tryacquire (int arg)

      A lot of people just see this method, will not have a face confused force feeling, method body incredibly just return an exception, good business logic code it?

      Back to what we said at the outset, Aqs is actually just a synchronous component of the basic framework, the implementation of the specific to the custom Synchronizer to implement itself, so there is only one exception in this method.

This method is implemented by a user-defined Synchronizer, attempting to obtain an exclusive resource, returning true if successful, or false if it fails

      protected boolean tryAcquire(int arg) {          throw new UnsupportedOperationException();      }
  • Addwaiter (Node mode)

    Adds the current thread to the end of the CLH queue, and specifies exclusive mode.

    Node has two modes, the exclusive mode and the shared mode, which are Node.EXCLUSIVENode.SHARED

    Private node Addwaiter (node mode) {       //creates a node node in the specified mode with the current thread        node N   Ode = new Node (Thread.CurrentThread (), mode);        //Try The fast path of Enq;  Backup to full Enq on failure        node pred = tail; Get the tail of the queue to the variable pred        if (pred! = null) {//If the end of the team is not empty            node  . prev = pred;    Point the current node's predecessor node to the original tail            if (Compareandsettail (pred, node)) {//CAs to set tail to node                /*                * if set  Successful, indicating that no other thread has succeeded in this operation */               pred.next = node;  Point the back node of the original tail node to node                return node;  Back to node          }      }        ENQ (node);        return node;  }
  • Enq (Node)

    The method is to insert the node into the tail of the CLH queue and to ensure that the node nodes are correctly added via Spin (dead Loop)

      Private node Enq (final node node) {       for (;;)  {//spin--Dead Loop Add node            node t = tail; Get original tial node to t variable            if (t = = null) {//must initialize queue is empty     &NBS P          if (Compareandsethead (new node))//Set up an empty node as head node         &NBS P           tail = head;  Head and tail are the same node          } else {//queue not empty normal conditions                 node.prev = t; Set the predecessor node of the current node to the original tail node                if (Compareandsettail (t, node)) {//via CAs  The current node is tail node                    t.next = node;  The original tail node is the current node                    return t; Return to original tail node end loop               &NBSp        }      }  }
  • Acquirequeued (final node node, int arg)

    Coming to this method proves that the synchronization state has failed through Tryacquire, and the Addwaiter method has been called to add the current thread to the end of the CLH queue, and the rest is to wake itself up to get the synchronization status in the wait state.

    For threads that are already in the CLH queue, the synchronization state is acquired in an exclusive and non-interruptible mode.

      Final Boolean acquirequeued (final node node, int arg) {   boolean failed = true;  Successfully get the status of the resource    try {       boolean interrupted = false; Whether the state is interrupted        for (;;)  {//spin--dead loop            final Node p = node.predecessor (); Gets the front node of the current node            //If the predecessor node is the first node and has successfully obtained the synchronization status if (P = = head && tryacquire (AR  g) {               sethead (node); Sets the current node as the head node, and resets the current node node's predecessor to null                p.next = null;    Set the back node of the original head node to NULL, convenient GC reclaim the original head node failed = false;                return interrupted; Returns whether it is interrupted          }            //get synchronization status failed, determine if you need to block or interrupt &NB Sp          if (Shouldparkafterfailedacquire (p, node) &&   &nbsP            parkandcheckinterrupt ())               &NBSP  ; interrupted = true; If interrupted, set mark as true      }  } finally {       if (failed)   &NBSP ;         cancelacquire (node); Cancels the current node's attempt to continue to get synchronization status  }}
  • Shouldparkafterfailedacquire (node pred, node node)

    For nodes that have failed to get state, check and update their state and return true if the thread is blocked, which is the signal control method for all get state loops.

    Request Pred = = Node.prev

In fact, unless the lock gets successful, it will be blocked.

 private static Boolean Shouldparkafterfailedacquire (node pred, node node) {   int ws = PRED.W Aitstatus; Gets the status of the precursor node   The  //status is-1, indicating that the successor node is already in the waiting wait state, and when the node is released or canceled, it notifies the successor if (ws = = node.signal)        return true;    //If the status is greater than 0--, skip the node loop and find a node that is not in the cancel State    if (ws > 0) {      & Nbsp;do {           node.prev = pred = Pred.prev;      } while (Pred.waitstatus > 0);        //The successor of Pred is node node        pred.next = node;  } else {//If the status is less than 0        //must be propagate or 0--indicates no status, when 2 is in the condition queue nbsp      //set pred node status to signal        compareandsetwaitstatus (pred, WS, Node.signa via CAs) L);  }    return false; }
  • Parkandcheckinterrupt ()

    Also, when the node's predecessor node State is signal, it is possible to pack the node in the thread, otherwise it cannot pack the threads.

      private final boolean parkAndCheckInterrupt() {      //通过LockSupport工具阻塞当前线程      LockSupport.park(this);      return Thread.interrupted();  //清除中断标识,返回清除前的标识  }
  • Cancelacquire (Node node)

    The method is to cancel the node's access to the synchronization state of the thread, that is, the state of the node is changed to cancelled.

      private void Cancelacquire (node node) {   //Ignore if node doesn ' t exist    if (node = = null)  The node is empty returns        return;   ?     node.thread = null;   is the node online Cheng null?      //Skip cancelled predecessors    //get the node node's precursor nodes    node pred = Node.prev;    //Loop to get the status of the precursor node and find the first predecessor node with a status of not cancelled    while (pred.waitstatus > 0)       &N  Bsp;node.prev = pred = Pred.prev;   ?    //Prednext is the apparent node to Unsplice.  CASes below would    //fail if not, in which case, we lost race vs another cancel    //or signal,   So no further action is necessary.      //Gets the successor node of the pred node    node prednext = Pred.next;     //to set node node status to CANCELLED    node.waitstatus = node.cancelled;   ?    //If the node is a tail node, set the tail node through CAs to pred    if (node = = Tail && compareandsettail (noDE, pred)) {       //to set the next node of the pred node through CAS null        compareandsetnext (pred,   Prednext, NULL);    } else {//If not tail node?         int ws;   Initialize node node state variables        /*        * If pred is not a head node and the status is signal or the state is less than 0 and set pred        * status for signal success,. And the thread that pred encapsulates is not empty        */       if (pred! = Head &&     & nbsp       ((ws = pred.waitstatus) = = Node.signal | |             (ws <= 0 && compareandsetwaitstatus (pred, WS, node.signal)) &&am   P               pred.thread! = null) {           //get node node successor              node next = Node.next;            //If the successor node is null and the status is not cancelled           &NBSP;IF (Next! = null && next.waitstatus <= 0)                //set pred successor to NE   XT, that is, the successor of Pred is no longer node                compareandsetnext (pred, Prednext, next);       } else {           unparksuccessor (node);   Release successors      }?        node.next = node; Help GC  }}
  • Unparksuccessor (node node)
      private void Unparksuccessor (node node) {   //gets the status of node nodes    int ws = Node.waitstat       us    if (WS < 0)//If the status is less than 0, SIGNAL-1 or CONDITION-2 or PROPAGATE-3        //via CAS will n      Ode node status is set to 0        compareandsetwaitstatus (node, WS, 0);      ?       Gets the node node's successor    node s = node.next;    //If the successor node is empty or the state is greater than 0--cancelled   &NBSP;IF (s = = NULL | | s.waitstatus > 0) {    & nbsp        //successors to null        s = null;        //forward from tail node        for (node t = tail; t! = null && t! = Node       t = T.prev)            if (t.waitstatus <= 0)//judgment status is less than or equal to 0, in order to find a node with a status other than cancelled                 s = t; Find the top state less than or equal to 0 nodes  }    if (s! = null)//If the method aboveThe found node is not empty        //the S-node encapsulated thread is freed through the Locksupport tool        locksupport.unpark (      S.thread); }

After the above analysis, I think we have a better understanding of the into row code, then we can also try to draw a into row flowchart.

Dequeuing

The operation of the dequeue is really simple compared to into row, after all, into row need to consider the factors too much, to consider the precursor and the successor node, but also to consider the state of the node and so a bunch of factors, and the CLH queue is referred to the head node, so the factors will be a lot less trouble.

Release (int arg)

We don't say much nonsense, just go to the code.

This also frees the object in exclusive mode.

  public final boolean release(int arg) {      if (tryRelease(arg)) {          Node h = head;  //获取head节点          //如果head节点不为空并且状态不为0,也就是初始节点  if (h != null && h.waitStatus != 0)               unparkSuccessor(h);  //唤醒后继节点          return true;      }      return false;  }
  • tryrelease (int arg)

    This method, like into Row's tryacquire, is only one exception, which proves that the method is also implemented by the custom synchronization component itself, and only defines a method in the Aqs Synchronizer.

      protected boolean tryRelease(int arg) {      throw new UnsupportedOperationException();  }
  • Unparksuccessor (Node node)

    This method was actually spoken at the time of into row, and I moved the code above directly to explain it.

      private void Unparksuccessor (node node) {   //gets the status of node nodes    int ws = Node.waitstatus;    if (WS < 0)//If the status is less than 0, SIGNAL-1 or CONDITION-2 or PROPAGATE-3        //via CAS will node nodes  The point status is set to 0        compareandsetwaitstatus (node, WS, 0);  ?   Gets the node node's successor    node s = node.next;    //If the successor node is empty or the state is greater than 0--cancelled   &NBSP;IF (s = = NULL | | s.waitstatus > 0) {      &N   bsp;//successors to null        s = null;        //forward from tail node        for (node t = tail; t! = null && t! = node ; t = T.prev)            if (t.waitstatus <= 0)//judgment status is less than or equal to 0, just to find a node with a status of not cancelled &NB Sp               s = t; Find the top state less than or equal to 0 nodes  }    if (s! = null)//If the node found by the above method is not empty        //by Locksuppor T tool releases S-node encapsulated threads   &nbsp     locksupport.unpark (S.thread); }

    This is the release of the code, which is actually not very difficult to look at.

Summary

Spent a full 3 days or so to see the source of the Aqs, will go to see is also purely want to put their own knowledge of the concurrency can be enriched, but this time to see the source code is still not very smooth, because many codes or methods, separate to see the time may be able to understand, the role of the feeling method is really so, But when a whole process is strung up, it's not quite clear how this is done, and the entire execution process. There is no way to understand the code in the spin, and what happens each time the execution will affect the CLH queue.

However, oneself also has the harvest, at least compared to at the beginning, oneself to the Aqs have a little fur understanding, not later smell completely is ask Sanbuzhi state.

At the same time I also hope that this article will be able to understand the Aqs of the program Ape can play a role in the future, they will also be some of their own learning experience or information to share.

Resources

Fang Fei: "The Art of Java concurrent programming"

If you want to reprint, please be sure to indicate the source, after all, a piece of brick is not easy to move.

Java Concurrency Aqs Synchronizer learning

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.