All source code for this blog is from JDK 1.8
Before lock, we use synchronized to control synchronization, and the Wait (), notify () series method with object can implement the waiting/notification mode. After Java SE5, Java provides a lock interface, which provides a conditional condition for synchronized, and more detailed and flexible thread waits and wake-up operations. is a comparison of the condition and object monitor methods (excerpt from the Art of Java Concurrency programming):
Condition provides a series of methods to block and wake up threads:
- await () : Causes the current thread to wait until it receives a signal or is interrupted.
- await (long time, timeunit unit) : Causes the current thread to wait until it receives a signal, is interrupted, or arrives at a specified wait time.
- Awaitnanos (Long nanostimeout) : Causes the current thread to wait until it receives a signal, is interrupted, or arrives at a specified wait time. The return value indicates the time remaining, if awakened before Nanostimesout, then the return value = nanostimeout-time spent, if the return value <= 0, you can assume that it has timed out.
- awaituninterruptibly () : Causes the current thread to be in a wait state until the signal is received. "Note: This method is insensitive to interrupts."
- Awaituntil (Date deadline) : Causes the current thread to wait until it receives a signal, is interrupted, or arrives at a specified deadline. Returns true if it is not notified at the specified time, or returns False if the specified time is indicated.
- signal (): Wakes up a waiting thread. The thread must obtain a condition-related lock before returning from the wait method.
- signal ()all: Wakes all waiting threads. The thread that can be returned from the wait method must obtain the lock associated with the condition.
Condition is a conditional queue in a generalized way. He provides a more flexible wait/notification pattern for threads, and threads perform a pending operation after calling the await method, until a thread waits for a condition that is true to be awakened. Condition must be used with locks, because access to shared state variables occurs in a multithreaded environment. An instance of condition must be bound to a lock, so condition is generally used as the internal implementation of lock.
The realization of condtion
Gets a condition must pass the lock's Newcondition () method. The method is defined below the interface Lock, and the result returned is a new Condition instance bound to this lock instance. Condition is an interface, there is only one implementation class Conditionobject, because condition operation needs to acquire the relevant lock, and AQS is the implementation basis of the synchronous lock, so conditionobject is defined as the inner class of Aqs. Defined as follows:
publicclass ConditionObject implements Condition, java.io.Serializable {}
Wait queue
Each Condition object contains a FIFO queue, which is the key to the Condition object notification/wait feature. Each node in the queue contains a thread reference, which is the thread that waits on the condition object. We see that the definition of condition is clear:
Public class conditionobject implements Condition, java. io. Serializable { Private Static Final LongSerialversionuid =1173984872572414699L//Head node Private transientNode Firstwaiter;//Tail node Private transientNode Lastwaiter; Public Conditionobject() { }/** ellipsis method **/}
From the above code you can see that condition has the first node (firstwaiter), tail node (lastwaiter). The current thread calls the await () method, which is constructed as a node in the current thread and joins the node to the tail of the queue. The structure is as follows:
node contains a reference to the current thread. node defines the same class (Abstractqueuedsynchronized.node static inner Class) used by nodes that define the CLH synchronization queue with Aqs.
The queue structure of condition is simpler than the structure of CLH synchronization queue, the new process is simpler simply point the nextwaiter of the original tail node to the new node, and then update the Lastwaiter.
Wait
Calling the await () method of condition causes the current thread to enter a wait state, and joins the condition waiting queue to release the lock simultaneously. When returned from the await () method, the current thread must have acquired a condition related lock.
Public Final void await()throwsinterruptedexception {//When the front-line is interrupted if(Thread.interrupted ())Throw NewInterruptedexception ();//When the front-thread joins the waiting queueNode node = Addconditionwaiter ();//Release lock Longsavedstate = fullyrelease (node);intInterruptmode =0;/** * Detects if the thread on this node is on the sync team, if it is not, then the thread is not eligible to compete for a lock, continue waiting * Until this node is detected on the sync queue * * while(!isonsyncqueue (node)) {//thread hangsLocksupport.park ( This);//If it has been interrupted, exit if((Interruptmode = checkinterruptwhilewaiting (node))! =0) Break; }//Competitive sync status if(Acquirequeued (node, savedstate) && interruptmode! = throw_ie) Interruptmode = Reinterrupt;//Clean up the nodes in the condition queue that are not in the wait condition if(Node.nextwaiter! =NULL)//Clean up if cancelledUnlinkcancelledwaiters ();if(Interruptmode! =0) reportinterruptafterwait (Interruptmode); }
The logic of this code is to first create a new node of the current thread into the condition queue at the same time, and then release the synchronization state held by the current thread. Then it is constantly detecting that the node represents the release of the thread that appears in the CLH synchronization queue (which is detected in the Aqs queue after receiving the signal signal) and is suspended if it does not exist or participates in a competing synchronization state.
Join the Conditional queue (Addconditionwaiter ()) source code as follows:
PrivateNodeAddconditionwaiter() {Node t = lastwaiter;//Tail node //node node State if not condition, indicates that the node is not in a wait state and needs to clear the node if(t! =NULL&& t.waitstatus! = node.condition) {//Clears all nodes in the condition queue that are not conditionUnlinkcancelledwaiters (); t = lastwaiter; }//When a new node is created on the front thread, state conditionNode node =NewNode (Thread.CurrentThread (), node.condition);/** * Add the node to the last position in the condition queue * / if(T = =NULL) Firstwaiter = node;Elset.nextwaiter = node; lastwaiter = node;returnNode }
The main method is to add the current thread to the condition condition queue. Of course, before joining the tail node, you will know all the nodes that are not condition.
Fullyrelease (node node) is responsible for releasing locks held by the thread.
Final LongFullyrelease (node node) {BooleanFailed =true;Try{//Node status-in fact, the number of locks held Longsavedstate = GetState ();//Release lock if(Release (savedstate)) {failed =false;returnsavedstate; }Else{Throw NewIllegalmonitorstateexception (); } }finally{if(failed) Node.waitstatus = node.cancelled; } }
Isonsyncqueue (Node node): Returns True if a node has just started on the conditional queue and now acquires a lock on the synchronization queue
finalboolean isOnSyncQueue(Node node) { //状态为Condition,获取前驱节点为null,返回false ifnull) returnfalse; //后继节点不为null,肯定在CLH同步队列中 ifnull) returntrue; return findNodeFromTail(node); }
Unlinkcancelledwaiters (): Responsible for deleting nodes in the condition queue that do not have a status of condition
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;ElseTrail.nextwaiter = Next;if(Next = =NULL) Lastwaiter = Trail; }ElseTrail = t; t = Next; } }
Notice
Calling condition's signal () method will wake up the node waiting for the longest time in the waiting queue (the first node in the condition queue) and move the node to the CLH synchronization queue before waking the node.
publicfinalvoidsignal() { //检测当前线程是否为拥有锁的独 if (!isHeldExclusively()) thrownew IllegalMonitorStateException(); //头节点,唤醒条件队列中的第一个节点 Node first = firstWaiter; ifnull) doSignal(first); //唤醒 }
The method first determines whether the current thread has acquired a lock, which is a precondition. The head node in the condition queue is then awakened.
Dosignal (node first): Wake-up head node
privatevoiddoSignal(Node first) { do { //修改头结点,完成旧头结点的移出工作 ifnull) null; null; while (!transferForSignal(first) && null); }
Dosignal (node first) mainly does two things: 1. Modify the head node, 2. Call the Transferforsignal (node first) method to move the node to the CLH synchronization queue. The source code for transferforsignal (Node first) is as follows:
final boolean transferforsignal (node node) {//changes the node from state condition to initial state 0, if (!compareandsetwaitstatus (node, node.condition, 0 ) ) return false ; //the node into the SYN queue and returns a node in front of the node in the SYN queue Node P = Enq (node); int ws = P.waitstatus; //if the status of node P is cancel or modify Waitstatus fails, the is awakened directly. if (ws > 0 | |!compareandsetwaitstatus (P, WS, node.signal)) Locksupport.unpark (Node.thread); return true ; }
The entire notification process is as follows:
- Determines whether the current thread has acquired a lock and throws an exception directly if no fetch is obtained because the lock is the precondition for the notification.
- If the thread has acquired a lock, the first node of the conditional queue is awakened
- Waking the first node is to move the head node out of the conditional queue first, and then call Aqs's Enq (node node) method to safely move it to the CLH synchronization queue
- Finally, if the synchronization state of the node is cancel, or if the modification state is signal failure, the thread that Locksupport wakes the node is called directly.
Summarize
After a thread acquires the lock, by calling condition's await () method, the current thread is first added to the condition queue, then the lock is released, and finally the node is constantly self-checking to see if the nodes are already in the CLH synchronization queue. If yes, try to acquire the lock, otherwise it hangs. When the thread calls the signal () method, the program first checks whether the current thread acquires the lock, and then wakes the first node of the CLH synchronization queue through the Dosignal (Node first) method. The awakened thread will exit from the while loop in the await () method and then call the Acquirequeued () method to compete for the synchronization state.
Application of condition
Only know the principle, if you do not know to use the pit father, the following is the implementation of condition with the producer of consumer issues:
Public class conditiontest { PrivateLinkedlist<string> buffer;//Container Private intMaxSize;//Container Max . PrivateLock lock;PrivateCondition fullcondition;PrivateCondition notfullcondition; Conditiontest (intMaxSize) { This. maxSize = maxSize; Buffer =NewLinkedlist<string> (); Lock =NewReentrantlock (); Fullcondition = Lock.newcondition (); Notfullcondition = Lock.newcondition (); } Public void Set(String string)throwsinterruptedexception {lock.lock ();//Get lock Try{ while(MaxSize = = Buffer.size ()) {notfullcondition.await ();//full, the added thread goes into a wait state} buffer.add (String); Fullcondition.signal (); }finally{Lock.unlock ();//Remember to release the lock} } PublicStringGet()throwsinterruptedexception {string string; Lock.lock ();Try{ while(buffer.size () = =0) {fullcondition.await (); } string = Buffer.poll (); Notfullcondition.signal (); }finally{Lock.unlock (); }returnString }}
"Dead java Concurrency"-----J.U.C's condition