Juc Source Analysis 8-locks-aqs-condition

Source: Internet
Author: User

Aqs's Conditionobject implements a function similar to the object's Wait/notify/notify function, which is presumably:

1.object maintains a monitor and a wait queue, condition can have multiple condition for a lock, and maintains a conditional queue for each condition;

2. Provide Wait/signal/signalall function.

Get an introductory Demo:

public class Conditiontest {private static Reentrantlock lock = new Reentrantlock ();        private static Condition Condition = Lock.newcondition (); public static void Main (string[] args) {new Runnable () {@Override public void Ru                    N () {try {lock.lock ();                    System.out.println (Thread.CurrentThread () + "wait condition complete");                Condition.await ();                } catch (Interruptedexception e) {e.printstacktrace ();                    } finally {System.out.println (Thread.CurrentThread () + "Finally waits for the condition to complete, gogogo");                Lock.unlock ();        }}). Start ();                    Thread B = new Thread (new Runnable () {@Override public void run () {try {                    Lock.lock ();                    Condition.signalall (); System.out.println (Thread.CurrentThread () + "condition complete, release it");                } finally {Lock.unlock ();        }            }        });    B.start (); }}

Conditionobject Implementationcondition interface, condition provides a method definition:

There is no sense of familiarity.


Conditionobject each new will maintain a conditional queue, through node's nextwaiter string up

/** The first node of the conditional queue */private The last node of the transient node firstwaiter;/** conditional queue */private transient node lastwaiter;/** * Empty construction, see reent Rantlock.newcondition () Each time new Conditionobject () can maintain multiple conditional queues */public conditionobject () {}

Look at the await () process with interrupted response
/** the thread that responds to an interrupted await can invoke the await method must obtain the lock */public final void await () throws interruptedexception {//thread interrupts the direct exception if (Thread.interru    Pted ()) throw new Interruptedexception ();//encapsulates the current thread into the condition conditional queue node node = Addconditionwaiter ();    Release the node in the AQS synchronization wait queue int savedstate = fullyrelease (node);    int interruptmode = 0; See if the node is still in the Aqs synchronous wait queue, because Signal/signalall call will add the node to the Aqs waiting queue, if not there is the need for park while (!isonsyncqueue (node)) {//        If not, then it should be in the conditional queue, then Park Bar Locksupport.park (this);            After being awakened by Signal/signalall, check the interrupt status if interrupted, break, no if ((Interruptmode = checkinterruptwhilewaiting (node))! = 0)    Break }//Here is a description of the queue that has been added to the AQS, re-acquire, note that the acquirequeued return value is interrupt, return true is definitely interrupted, return False if (acquirequeued (node, savedstate) &am    p;& Interruptmode! = throw_ie) Interruptmode = Reinterrupt;    if (node.nextwaiter! = null)//clean up if cancelled unlinkcancelledwaiters (); if (Interruptmode! = 0)//break, direct throw or set interrupt state ReportinterruptafteRwait (Interruptmode);} /** first judge the state of Lastwaiter, if it is not condition to go through the condition queue, all the state is not condition removed and then add the node to Lastwaiter (Aqs like tail) Nextwaiter, If last is null, the nextwaiter of both first and final points to the new node and finally lastwaiter points to the new join node */private node Addconditionwaiter () {Node T =    Lastwaiter;    If Lastwaiter is cancelled.        if (t! = null && t.waitstatus! = node.condition) {unlinkcancelledwaiters ();    t = lastwaiter;    } node node = new node (Thread.CurrentThread (), node.condition);    if (t = = null) firstwaiter = node;    else T.nextwaiter = node;    lastwaiter = node; return node;} /** start from the Firstwaiter, filter out all the status of the node is not condition by the Trail-t-next node to move backwards, t from the beginning of the firstwaiter at that time, take the paper to draw once again only clear */private void    Unlinkcancelledwaiters () {Node t = firstwaiter;    Node trail = null;        while (t! = null) {Node next = T.nextwaiter;            if (t.waitstatus! = node.condition) {t.nextwaiter = null;            if (trail = = null) Firstwaiter = next; Else               Trail.nextwaiter = Next;        if (next = = null) Lastwaiter = Trail;        } else Trail = t;    t = Next; }}/** here is the release Aqs the node in the synchronization wait queue returns the state value before the release has an exception, changing the status of the node to cancelled*/final int fullyrelease (node node) {Boolean failed = True    ;        try {int savedstate = GetState ();            if (Release (savedstate)) {failed = false;        return savedstate;        } else {throw new illegalmonitorstateexception ();    }} finally {if (failed) Node.waitstatus = node.cancelled; }}/** See if the node is still in Aqs's queue */final Boolean Isonsyncqueue (node node) {if (Node.waitstatus = Node.condition | | node.prev = = NUL    L) return false;    if (node.next! = null)//If has successor, it must is on queue return true; /* Before analyzing the order of Aqs join Nodes Enq (), Pre-tail-next,pre joined, but does not indicate that the node is really in the Aqs waiting queue, so you need to filter from the tail to see if there is a */return Findno Defromtail (node);} /** from tail forward to determine whether the node is in the queue, find the return true*/private Boolean Findnodefromtail (node node) {node t = tail; for (;;)        {if (t = = node) return true;        if (t = = null) return false;    t = T.prev; }}/** 2 states, indicating that await is awakened, if the check thread is interrupted, you need to determine when it was interrupted, and then determine how to return the interrupt, whether it is a direct exception or set the interrupt state */private static final int reinterrupt = 1 ;p rivate static final int throw_ie = -1;/** Check interrupt status, 0: Not interrupted, throw_ie:reinterrupt: */private int checkinterruptwhilewaiting (        Node node) {return thread.interrupted ()? (Transferaftercancelledwait (node)?) Throw_ie:reinterrupt): 0;} /** Check if the interrupt occurred, before signal or signal after */final Boolean transferaftercancelledwait (node node) {//If you call signal, First set the state of the node to 0, and then transfer the node from the conditional queue (ENQ) to the Aqs waiting queue//So the following CAS is successful, then this interrupt must occur before signal if (Compareandsetwaitstatus (node,      Node.condition, 0)) {//Put the node into the Aqs queue to ensure that the acquirequeued executes Enq (node);  return true;  }/* * If we lost out to a signal () and then we can ' t proceed * until it finishes it Enq (). Cancelling during an * incomplete transfer is BoTh rare and transient, so just * spin. *//** here, it must have happened signal, but Signal's enq did not complete, so spin, let signal enq complete, return false*/while (!isonsyncqueue (node)) Thread.yield  (); return false;} /** This is based on the previous identification to determine how to handle the interrupt signal before the throw, signal set the interrupt state */private void reportinterruptafterwait (int interruptmode) throws    interruptedexception {if (Interruptmode = = Throw_ie) THROW new Interruptedexception (); else if (Interruptmode = = Reinterrupt) selfinterrupt ();}
The process for the entire await is:

1. Determine thread interruption, interrupt direct throw exception
2. Join the node to the condition condition queue
3. Release the lock in the Aqs queue
4.while to determine if the queue is waiting in Aqs
5. If you are not in the Aqs queue, park
6. Wake-up check is signal wake-up or interrupt wake-up
7. Interrupt wakeup to determine the signal before or after signal, set how to handle the interruption, signal before the words also need to enq the node to Aqs waiting queue, go to 4
8. If in the acquirequeued, regain, here to determine acquire return, true is interrupt, and then set the interrupt processing mode
9. If the node's nextwaiter is not NULL, it cleans up the condition condition queue and clears all nodes that are not condition in the state.
10. Finally see if there is a need to deal with interrupts, if any, signal before the interrupt directly thrown, after signal set the interrupt state.

The basic flow of awaitnanos/awaituntil/await (long time, Timeunit unit) is almost the same as an await in response to an outage, except that there is more time-out, and there is no difference between the response timeout mentioned earlier, which is the underlying unsafe.


Look at the Signal/signalall:

Public final void signal () {if (!isheldexclusively ()) throw new Illegalmonitorstateexception ();    Node first = Firstwaiter; if (first! = NULL) dosignal (first);} The/** subclass implements the judgment whether it is own */protected Boolean isheldexclusively () {throw new Unsupportedoperationexception ();} /** here releases a condition state node from first */private void dosignal (node first) {do {if (Firstwaiter = first.nextwaiter)        = = NULL) Lastwaiter = NULL;    First.nextwaiter = null; } while (!transferforsignal (first) && (first = firstwaiter) = null);}     Final Boolean Transferforsignal (node node) {/* * * If cannot change waitstatus, and the node has been cancelled.    *//Set node status to 0 if (!compareandsetwaitstatus (node, node.condition, 0)) return false; /* * Splice onto queue and try to set waitstatus of predecessor to * indicate, this thread is (probably) waiting. If cancelled or * Attempt to set Waitstatus fails, wake-to-resync (in which *Case the Waitstatus can transiently and harmlessly wrong).    *///Add the node to the Aqs waiting queue, and return the pre node P = Enq (node) to join the nodes;    int ws = P.waitstatus; Set the node state to SIGNAL if it fails directly unpark the newly joined node if (ws > 0 | |!compareandsetwaitstatus (P, WS, node.signal)) locksupport.un    Park (Node.thread); return true;}
The signal only releases the first node that has a state of condition, and then joins the node to the Aqs synchronization wait queue, which sets the status of the pre for the new join node to signal. Look at the release of the Signalall:

private void Dosignalall (Node first) {    lastwaiter = Firstwaiter = null;    Do {        Node next = first.nextwaiter;        First.nextwaiter = null;        Transferforsignal (first);        First = next;    } while (first! = null);}
See that Signalall is ultimately processing all condition nodes.

In fact, whether the await or signal/signalall are simulated object.wait and notify/notifyall, can be compared to see.

Aqs probably so much, there is a abstractqueuedlongsynchronizer this class, and Aqs almost, just state states using a long type:

    private volatile long state;

Aqs is used to:

    private volatile int state;


Attention:

An await will have a false wake-up condition, even if a thread without signal,await may be awakened. Reference: discussion of conditional variables and false wakeup (spurious wakeup) in multithreaded programming http://siwind.iteye.com/blog/1469216, the final recommendation is to use While judging the condition rather than using the If judgment:

while (condition not satisfied) {
Condition_wait (cond, mutex);
}
Instead of:
If (condition not satisfied) {
Condition_wait (Cond,mutex);
}

To tell the truth finally I did not understand what causes the false wake, and then went to StackOverflow query the next, this is an explanation, self-study it

Http://stackoverflow.com/questions/1050592/do-spurious-wakeups-actually-happen, and this piece of http://blog.sina.com.cn/s/. Blog_e59371cc0102v29b.html


Reference:

http://blog.csdn.net/yuenkin/article/details/50867530

http://brokendreams.iteye.com/blog/2250372

http://ifeve.com/understand-condition/comment-page-1/#comment-26901

Juc Source Analysis 8-locks-aqs-condition

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.