Turn from: http://www.importnew.com/9281.html
In the Java.util.concurrent package, there are two very special tool classes, condition and Reentrantlock, which are known to people who have been used, and reentrantlock (reentrant locks) are implementations of an exclusive lock provided by the JDK's concurrent package. It inherits from the Abstractqueuedsynchronizer (Synchronizer) of Dong Lea, exactly reentrantlock an inner class inherits Abstractqueuedsynchronizer, Reentrantlock is nothing more than a proxy for the class, and one might ask why the inner class is used in the wrapper layer. I think it is a safe relationship, because there are many methods in Abstractqueuedsynchronizer, but also implemented a shared lock, Condition (later detail) and other functions, if the direct reentrantlock inherit it, It is very easy to show that the API in Abstractqueuedsynchronizer is useless.
So, today, we're going to discuss the implementation of the Condition tool class.
Reentrantlock and condition are usually used in this way:
publicstatic void Main (string[] args) {Finalreentrantlock reentrantlock = Newreentrantlock ();
Finalcondition condition = reentrantlock.newcondition ();
Thread thread = Newthread ((Runnable) ()-> {try{reentrantlock.lock ();
SYSTEM.OUT.PRINTLN ("I want to wait for a new signal" + this);
Condition.wait ();
catch (Interruptedexception e) {e.printstacktrace (); System.out.println ("Get a signal.")
"+ this);
Reentrantlock.unlock ();
}, "WaitThread1");
Thread.Start ();
Thread thread1 = Newthread (Runnable) ()-> {reentrantlock.lock ();
System.out.println ("I Got the Lock");
try{Thread.Sleep (3000);
catch (Interruptedexception e) {e.printstacktrace ();
} condition.signalall (); SYSTEM.OUT.PRINTLN ("I sent a signal.")
"); reentrantlock.unLock ();
}, "Signalthread");
Thread1.start (); }
After running, the results are as follows:
I'm going to wait for a new signal lock. REENTRANTLOCKTEST$1@A62FC3
I got the lock,
I sent a signal.
get a signal.
can see that
The condition is executed when the await method is called in Thread 1, which releases the lock and sleeps itself, waiting to wake up,
Thread 2 gets to the lock, starts doing the work, completes, invokes the condition signal method, wakes the thread 1, thread 1 resumes execution.
The above description Condition is a tool class that coordinates communication between multithreading, making some, or some threads, wait for a certain condition (Condition), which is awakened only if the condition has (signal or Signalall method is called). And then scramble for the lock again.
So, how does it come true?
The first thing to understand is that reentrantlock.newcondition () returns an implementation of the condition, which is implemented in Abstractqueuedsynchronizer, called Newcondition ()
Publiccondition newcondition () {returnsync.newcondition ();}
It can access the methods in Abstractqueuedsynchronizer and the rest of the inner class (Abstractqueuedsynchronizer is an abstract class, as for how he can access, here is a wonderful point, I specifically use the demo description)
Now, let's take a look at the implementation of the condition class, or start with the demo above,
To make it easier to write, I'll abbreviate abstractqueuedsynchronizer to Aqs.
When await is invoked, the code is as follows:
publicfinal void await () throwsinterruptedexception {if (thread.interrupted ()) thrownew interruptedexception ()
; Node node = Addconditionwaiter ();
After the current thread is wrapped,//Added to a linked list that condition itself maintains.
Intsavedstate = fullyrelease (node)//release the current thread-occupied lock, see from demo,//call await before, current thread is possession lock
Intinterruptmode = 0;
While!isonsyncqueue (node) {//release is complete, traverse the Aqs queue to see if the current node is in the queue,//not to indicate that it is not qualified to compete for the lock, so continue to sleep.
Until it is added to the queue, smart you may guess,//no mistake, join in when Singal is OK.
Locksupport.park (this);
if ((Interruptmode = checkinterruptwhilewaiting (node))!= 0) break;
//Wake up, restart the formal competition lock, similarly, if the competition is not yet will sleep, waiting for the awakening to start again competition.
if (acquirequeued (node, savedstate) && interruptmode!= throw_ie) Interruptmode = Reinterrupt;
if (node.nextwaiter!= null) unlinkcancelledwaiters ();
if (interruptmode!= 0) reportinterruptafterwait (interruptmode); }
Back to the demo above, after the lock is released, thread 1 begins to slumber, this time the thread because thread 1 sleeps, wakes up the head node in the Aqs queue, so thread 2 will start to compete for the lock, and get to, wait 3 seconds, thread 2 will invoke signal method, "emit" signal signal, The signal method is as follows:
publicfinal void Signal () {
if (!isheldexclusively ())
thrownew illegalmonitorstateexception ();
Node-i = firstwaiter; Firstwaiter is the head node of a linked list maintained by condition itself,
//The wake operation begins after the first node has been removed (
if)
dosignal (a);
In fact, the Condition maintains the header and tail nodes of the waiting queue, which is used to hold the thread that waits for the signal signal, which is encapsulated in the node node and stored here.
Publicclass Conditionobject implementscondition, java.io.Serializable {
privatestatic final long Serialversionuid = 1173984872572414699L;
/** the condition queue. * *
privatetransient Node firstwaiter;
/** last node of condition queue. * *
privatetransient Node lastwaiter;
The key is this, and we know that Aqs's own queues are the queues that are currently waiting for the resource, and Aqs will wake up all the nodes in the queue from the previous to the other after the resource is freed, so that their corresponding thread resumes execution. Until the queue is empty.
And condition itself maintains a queue, the role of which is to maintain a queue waiting for signal signal, the role of two queues are different, in fact, each thread will only exist at the same time only one of the two queues, the process is this: When thread 1 invokes Reentrantlock.lock, the thread is added to the Aqs wait queue. When thread 1 invokes the await method, the thread is removed from the Aqs, and the corresponding operation is the release of the lock. It was then immediately added to the condition waiting queue, thinking that the thread needed a signal signal. Thread 2, because thread 1 releases the lock relationship, is awakened, and the judge can acquire the lock, so thread 2 acquires the lock and is added to the Aqs waiting queue. Thread 2 invokes the signal method, at which point the condition wait queue has only 11 nodes, so it is taken out and added to the Aqs wait queue. Note that thread 1 is not awakened at this time. The signal method completes, and thread 2 calls the Reentrantlock.unlock () method to release the lock. This time because the Aqs only thread 1, so, Aqs release the lock in the sequence from beginning to end to wake the thread, thread 1 is awakened, so thread 1 reply to execute. Until the release of the entire process is complete.
As you can see, the entire collaboration process is achieved by the nodes moving back and forth through the AQS waiting queues and condition waiting queues, condition as a condition class, maintaining a queue of waiting signals. And in due time, the node is added to the Aqs waiting queue to achieve the wake-up operation.
See here, the code of the signal method should not be difficult to understand.
Remove the head knot and then dosignal.
publicfinal void Signal () {if (!isheldexclusively ()) {thrownew illegalmonitorstateexception ();
Node-i = firstwaiter;
if (!= null) {dosignal (a);
} privatevoid dosignal (node first) {do{if ((Firstwaiter = first.nextwaiter) = null)//Modify header node, complete removal of old head node
Lastwaiter = null;
First.nextwaiter = null;
}while (!transferforsignal (first) &&//The Old Head node is added to the Aqs waiting queue (A (first = Firstwaiter)!= null);
} Finalboolean transferforsignal (node node) {/* * * If cannot change waitstatus, the node has been.
*/if (!compareandsetwaitstatus (node, node.condition, 0)) return false; * * Splice onto queue and try to set waitstatus the predecessor to * indicate this thread is (probably) waiting. IF cancelled or attempt * To set Waitstatus fails, wake up to Resync (in which case the * waitstatus can be tra
Nsiently and harmlessly wrong).
*/ Node P = Enq (node);
Intws = P.waitstatus;
If the node's status is cancel or if the modification waitstatus fails, it is awakened directly.
if (ws > 0| |!compareandsetwaitstatus (P, WS, node.signal)) Locksupport.unpark (Node.thread);
Returntrue; }
As you can see, the normal situation ws > 0 | | !compareandsetwaitstatus (P, WS, node.signal) This judgment is not true, so the thread will not be awakened at this time.
Only when the thread that sent the signal signal calls Reentrantlock.unlock () is awakened because it has been added to the Aqs waiting queue.