Condition source code analysis and waiting notification mechanism, interview questions for Java Senior Software Engineers

Posted by manishdugar on Wed, 15 Dec 2021 01:04:38 +0100



So we can see ConditionObject The waiting queue is managed by holding the head and tail pointers of the waiting queue. The main thing to note is Node Class reuse in AQS Medium Node Class, its node state and related attributes can be viewed[AQS Article on the implementation principle of]( ),If you read this article carefully, right condition It's easy to understand, right lock The implementation of the system will also have a qualitative improvement. Node Class has such an attribute:



//Successor node

Node nextWaiter;



For further clarification,**The wait queue is a one-way queue**,And before that AQS It is known that the synchronization queue is a two-way queue. Next, let's use one demo,adopt debug Go in and see if it is in line with our conjecture:



public static void main(String[] args) {

for (int i = 0; i < 10; i++) {

    Thread thread = new Thread(() -> {

        lock.lock();

        try {

            condition.await();

        } catch (InterruptedException e) {

            e.printStackTrace();

        }finally {

            lock.unlock();

        }

    });

    thread.start();

}

}



This code doesn't have any practical meaning, even stinks. It just wants to explain what we just thought. There are 10 new threads. No thread gets the lock first and then calls. condition.await Method to release the lock and add the current thread to the waiting queue debug Control the view when you go to the 10th thread`firstWaiter`That is, the head node in the waiting queue, debug The scenario under mode is as follows:



![debug Scenario map in mode](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0pvdXJXb24vaW1hZ2UvbWFzdGVyL0phdmElRTUlQjklQjYlRTUlOEYlOTElRTclQkMlOTYlRTclQTglOEItTG9jayVFNCVCRCU5MyVFNyVCMyVCQi9kZWJ1ZyVFNiVBOCVBMSVFNSVCQyU4RiVFNCVCOCU4QiVFNiU4MyU4NSVFNiU5OSVBRiVFNSU5QiVCRS5wbmc?x-oss-process=image/format,png)



From this figure, we can clearly see the following points: 1. call condition.await After the method, the thread tail is inserted into the waiting queue in turn, as shown in the figure. The thread references in the queue are Thread-0,Thread-1,Thread-2...Thread-8;2. The wait queue is a one-way queue. Through our conjecture and experimental verification, we can get the schematic diagram of the waiting queue, as shown in the figure below:



![Insert picture description here](https://img-blog.csdnimg.cn/20191214223918792.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly90aGlua3dvbi5ibG9nLmNzZG4ubmV0,size_16,color_FFFFFF,t_70)



At the same time, it should be noted that we can call multiple times lock.newCondition()Method to create multiple condition Object, that is, a lock Multiple waiting queues can be held. And before using Object The way is actually in**object Object There can only be one synchronization queue and one waiting queue on the object monitor, and there is no synchronization queue in the contract Lock Have one synchronization queue and multiple waiting queues**. The schematic diagram is as follows:



![AQS Hold multiple Condition.png](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0pvdXJXb24vaW1hZ2UvbWFzdGVyL0phdmElRTUlQjklQjYlRTUlOEYlOTElRTclQkMlOTYlRTclQTglOEItTG9jayVFNCVCRCU5MyVFNyVCMyVCQi9BUVMlRTYlOEMlODElRTYlOUMlODklRTUlQTQlOUElRTQlQjglQUFDb25kaXRpb24ucG5n?x-oss-process=image/format,png)



As shown in the figure, ConditionObject yes AQS So each ConditionObject Can access AQS The method provided is equivalent to each Condition All have references to the synchronizer to which they belong.



### [] () implementation principle of await



**When called condition.await()Method will make the current lock If the thread can enter the waiting queue from await()Method, the thread must get the condition Associated lock**. Next, we still look at it from the perspective of the source code. Only when we are familiar with the logic of the source code can we have the deepest understanding. await()The method source code is:



public final void await() throws InterruptedException {

if (Thread.interrupted())

    throw new InterruptedException();

// 1. Wrap the current thread as a Node and insert the tail into the waiting queue

Node node = addConditionWaiter();

// 2. Release the lock occupied by the current thread. During the release process, the next node in the synchronization queue will be awakened

int savedState = fullyRelease(node);

int interruptMode = 0;

while (!isOnSyncQueue(node)) {

	// 3. The current thread enters the waiting state

    LockSupport.park(this);

    if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)

        break;

}

// 4. Wait until the synchronization state is obtained (i.e. lock is obtained)

if (acquireQueued(node, savedState) && interruptMode != THROW_IE)

    interruptMode = REINTERRUPT;

if (node.nextWaiter != null) // clean up if cancelled

    unlinkCancelledWaiters();

// 5. Handling interrupted situations

if (interruptMode != 0)

    reportInterruptAfterWait(interruptMode);

}



Main logic of the code**See the notes**,We all know**Current thread call condition.await()Method will cause the current thread to be released lock It is then added to the waiting queue until it is signal/signalAll This will cause the current thread to move from the waiting queue to the synchronization queue until it gets the lock Only after await Method returns, or is interrupted while waiting. Interrupt processing will be performed**. Then we will have several questions about the implementation process: 1. How is the current thread added to the waiting queue? two.The process of releasing the lock? three.How can I get from await Method exit? The logic of this code is to tell us the answers to these three questions. specific**See the notes**,Call in the first step addConditionWaiter Add the current thread to the waiting queue. The source code of this method is:



private Node addConditionWaiter() {

Node t = lastWaiter;

// If lastWaiter is cancelled, clean out.

if (t != null && t.waitStatus != Node.CONDITION) {

    unlinkCancelledWaiters();

    t = lastWaiter;

}

//Wrap the current thread as a Node

Node node = new Node(Thread.currentThread(), Node.CONDITION);

if (t == null)

    firstWaiter = node;

else

	//Tail insertion

    t.nextWaiter = node;

//Update lastWaiter

lastWaiter = node;

return node;

}



This code is easy to understand and wraps the current node into Node,If waiting on the queue firstWaiter by null If the waiting queue is empty, the firstWaiter Point to the current Node,Otherwise, update lastWaiter(Tail node)Just. namely**Encapsulates the current thread by tail insertion Node Just insert it into the waiting queue**,At the same time, we can see that the waiting queue is a**Chained queue without leading node**,We studied before AQS Know synchronization queue when**It is a chain queue of leading nodes**,This is a difference between the two. Inserting the current node into the wait pair column frees the current thread lock,from fullyRelease Method implementation, fullyRelease The source code is:



final int fullyRelease(Node node) {

boolean failed = true;

try {

    int savedState = getState();

    if (release(savedState)) {

		//Successfully released sync status

        failed = false;

        return savedState;

    } else {

		//An exception is thrown if the synchronization state is not released successfully

        throw new IllegalMonitorStateException();

    }

} finally {

    if (failed)

        node.waitStatus = Node.CANCELLED;

}

}



This code is easy to understand,**call AQS Template method for release Method release AQS And wake up the thread referenced by the successor node of the head node in the synchronization queue**,If the release is successful, it returns normally. If it fails, an exception is thrown. So far, these two pieces of code have solved the answers to the previous two questions, leaving the third question, how to start from await Method exit? Now look back await Method has the following logic:



while (!isOnSyncQueue(node)) {

// 3. The current thread enters the waiting state

LockSupport.park(this);

if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)

    break;

}



Obviously, when the thread first calls condition.await()Method will enter this while()In the loop, and then through LockSupport.park(this)Method to make the current thread enter the waiting state, you want to exit the await The first prerequisite for this method is naturally to exit this method first while Circulation, there are only two places left at the exit:**1\. Logic goes to break sign out while Circulation; two. while The logical judgment in the loop is false**. Let's look at the code. The condition of the first case is that the code will go after the current waiting thread is interrupted break Exit. In the second case, the current node is moved to the synchronization queue (i.e. called by another thread) condition of signal perhaps signalAll Method), while Logical judgment in false End after while Cycle.**To sum up**,namely**The current thread is interrupted or called condition.signal/condition.signalAll Method, after the current node is moved to the synchronization queue** ,This is the current thread exit await Preconditions of the method. When exiting while Called after the loop`acquireQueued(node, savedState)`,This method is introduced in AQS As mentioned in the bottom implementation of, you can go if you are interested[Look at this article]( ),The function of this method is to**During the spin process, the thread keeps trying to obtain the synchronization status until it succeeds (the thread obtains the synchronization status) lock)**. This also shows that**sign out await The method must have been obtained condition Referenced (associated) lock**. So far, we have completely found the answers to the first three questions by reading the source code await Deepen the understanding of methods. await The schematic diagram of the method is shown in the figure below:



![await Method diagram](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0pvdXJXb24vaW1hZ2UvbWFzdGVyL0phdmElRTUlQjklQjYlRTUlOEYlOTElRTclQkMlOTYlRTclQTglOEItTG9jayVFNCVCRCU5MyVFNyVCMyVCQi9hd2FpdCVFNiU5NiVCOSVFNiVCMyU5NSVFNyVBNCVCQSVFNiU4NCU4RiVFNSU5QiVCRS5wbmc?x-oss-process=image/format,png)



As shown in the figure, call condition.await The thread of the method must have been obtained lock,That is, the current thread is the head node in the synchronization queue. Calling this method will cause the Node The tail is inserted into the waiting queue.



> Timeout mechanism support



condition Additionally, the timeout mechanism is supported, and the user can call the method awaitNanos,awaitUtil. The implementation principle of these two methods is basically consistent with AQS Medium tryAcquire The method is the same, about tryAcquire You can read it carefully[This article]( ). 



> Non responsive interrupt support



To not respond to an interrupt, call condition.awaitUninterruptibly()Method. The source code of the method is:



public final void awaitUninterruptibly() {

Node node = addConditionWaiter();

int savedState = fullyRelease(node);

boolean interrupted = false;

while (!isOnSyncQueue(node)) {

    LockSupport.park(this);

    if (Thread.interrupted())

        interrupted = true;

}

if (acquireQueued(node, savedState) || interrupted)

    selfInterrupt();

}



This method is the same as above await The methods are basically the same, but the processing of interrupts is reduced and omitted reportInterruptAfterWait Method throws an interrupted exception.



### [] () implementation principle of signal/signalAll



**call condition of signal perhaps signalAll Method can move the node with the longest waiting time in the waiting queue to the synchronization queue**,So that the node can have the opportunity to obtain lock. According to the waiting queue, it is first in first out( FIFO)Therefore, the head node of the waiting queue must be the node with the longest waiting time, that is, each call condition of signal The method is to move the head node to the synchronization queue. Let's see if this conjecture is right by looking at the source code, signal The method source code is:



public final void signal() {

//1. First check whether the current thread has obtained lock

if (!isHeldExclusively())

    throw new IllegalMonitorStateException();

//2. Get the first node in the waiting queue. Subsequent operations are directed at this node

Node first = firstWaiter;

if (first != null)

    doSignal(first);

}



signal Method first detects whether the current thread has obtained lock,If not obtained lock An exception will be thrown directly. If it is obtained, the node referenced by the head pointer of the waiting queue will be obtained, and the subsequent operations will be completed doSignal The method is also based on this node. Let's take a look doSignal What did the method do, doSignal The method source code is:



private void doSignal(Node first) {

do {

    if ( (firstWaiter = first.nextWaiter) == null)

        lastWaiter = null;

	//1. Remove the head node from the waiting queue

    first.nextWaiter = null;

	//2. The transferForSignal method in while performs real processing on the header node

} while (!transferForSignal(first) &&

         (first = firstWaiter) != null);

}



Please refer to the notes for the specific logic. The real logic for processing the header node is**transferForSignal**The source code of this method is:



final boolean transferForSignal(Node node) {

/*

 * If cannot change waitStatus, the node has been cancelled.

 */

//1. The update status is 0

if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))

    return false;



/*

 * Splice onto queue and try to set waitStatus of predecessor to

 * indicate that thread is (probably) waiting. If cancelled or

last

There are also ways to brush questions for the interview. It is suggested that it is best to carry out according to the topic, and then from basic to advanced, from shallow to deep, the effect will be better. Of course, these contents are also sorted out in a pdf document and divided into the following topics:

  • Java Basics

  • Algorithm and programming

  • Database part

  • Popular frameworks and new technologies (Spring + spring cloud + spring cloud Alibaba)

Of course, there are more than these contents in this interview document. In fact, the interview contents of other parts such as JVM, design pattern, ZK, MQ and data structure are involved. Because of the length of the article, it is not all described here.

As a programmer, phased learning is essential and needs to maintain a certain continuity. This time, I systematically reviewed some key knowledge points at this stage, which not only consolidated my foundation, but also improved the breadth and depth of my knowledge.

Finally, let me remind you that if you want to learn, but you have no dry learning materials, you just need a lot of support

)]

  • Algorithm and programming

[external chain picture transferring... (img-K436HQrO-1630890938285)]

  • Database part

[external chain picture transferring... (img-lflqs3i7-16308909382886)]

  • Popular frameworks and new technologies (Spring + spring cloud + spring cloud Alibaba)

[external chain picture transferring... (img-flaj8lso-16308909382887)]

Of course, there are more than these contents in this interview document. In fact, the interview contents of other parts such as JVM, design pattern, ZK, MQ and data structure are involved. Because of the length of the article, it is not all described here.

As a programmer, phased learning is essential and needs to maintain a certain continuity. This time, I systematically reviewed some key knowledge points at this stage, which not only consolidated my foundation, but also improved the breadth and depth of my knowledge.

Finally, let me remind you that if you want to learn, but you have no dry learning materials, you just need a lot of support

CodeChina open source project: [analysis of Java interview questions of front-line large manufacturers + core summary learning notes + latest explanation Video]

Topics: Java Back-end Interview Programmer