In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-21 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article introduces the knowledge of "waiting and notification between Java concurrent threads". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!
Foreword:
Having talked about some of the principles of concurrent programming, we are going to learn about collaboration between threads. Generally speaking, the current thread needs to wait under certain conditions and does not need to use too many system resources. Under certain conditions, we need to wake it up and allocate it certain system resources to keep it working. In this way, resources can be better saved.
1. Wait () and notify () of Object
Basic concepts:
A thread that is asked to pause because the condition for executing the target action is not met is wait, and a thread that wakes up the paused thread after meeting the condition for executing the target action is notify.
Basic template:
Synchronized (obj) {/ / Protection condition does not hold while (flag) {/ / pause the current thread obj.wait ();} / / when the protection condition is established, jump out of the while loop and execute the target action doAction ();}
Parsing wait (): the purpose of Object.wait () is to pause the thread of execution, and the life cycle of the thread of execution is changed to WAITING. Note here that it waits indefinitely until the thread is awakened by the notify () method. The function of Object.wait (long timeout) is to make the thread of execution wake up automatically without being woken up for a certain period of time, that is, wait for a timeout. Object.wait (long timeout,int naous) is a more precise way to control time, which can be controlled to nanoseconds. It is important to note that wait () does not execute this method until the current thread has a lock and releases the lock owned by the current thread, thus putting the thread into a waiting state and other threads trying to acquire the current lock. That is, you need to apply for a lock and release the lock.
Parsing notify (): the Object.notify () method wakes up the thread that called wait (), only one at most. If there are multiple threads, we may not be able to wake up the thread we want. Object.notifyAll () wakes up all waiting threads. The notify method must notify the thread that the lock has been acquired before it can notify. The current notification thread after the notification needs to release the lock, which is then acquired by the waiting thread. So it involves a step of applying for and releasing the lock.
There are three major problems between wait () and notify ():
As can be seen from the above analysis, notify () is an undirected wake-up call, and notifyAll () is an unbiased wake-up call. So there are three problems.
Wake up prematurely: suppose that there are currently three groups of waiting threads (w1memw2pw3) and notification threads synchronized on the object obj. The judgment wakeup condition of w1scorew2 is the same, and the condition is updated and awakened by thread N1, while that of w3 is different, and the condition is updated and awakened by N2. If N1 executes wake-up, then notify cannot be executed, because two threads need to be woken up, only notifyAll () can be used. However, after being used, the w3 is woken up when the conditions are not met, and it needs to take up resources all the time to wait for execution.
Signal loss: this problem is mainly caused by the programmer programming problem, not the internal implementation mechanism. When programming, if you use notify () where notifyAll () should be used, only one thread can be awakened, so that other threads that should be woken up will not be awakened, which is signal loss. If the waiting thread does not determine whether the protection condition is established before executing the wait () method, it will be notified that the waiting thread has updated the relevant shared variables and executed the notify () method before the waiting thread enters the critical section, but because wait () has not been executed and the judgment of the shared variable has not been set, the wait () method will be executed, resulting in the thread waiting all the time and losing a signal.
Deceptive wake-up: waiting threads do not have to have notify () / notifyAll () to wake up, although the probability is very low, but the operating system allows this to happen.
Context switching problem: first, wait () will at least cause the thread to apply for and release the internal lock of the corresponding object. When notify () / notifyAll () is required to hold the corresponding internal lock of the object and the lock will also be released, the problem of context switching will occur, that is, the change from RUNNABLE state to non-RUNNABLE state will occur.
Solution to the problem:
Signal loss and deceptive wake-up problems: both can be avoided using while loops, as written in the template above.
Context switching problem: using notify () instead of notifyAll () while ensuring the correctness of the program, notify will not cause premature awakening, so context switching is reduced. And after using notify, you should release the corresponding internal lock as soon as possible, so that wait () can apply for the lock more quickly.
Wake up early: use await and signal in java.util.concurrent.locks.Condition.
PS: since wait and notify in Object use the native method, that is, C++, no source code parsing is done here.
2. Await () and signal () in Condition
This method correspondingly changes the undirected problem mentioned above, and a queue is maintained within each Condition, which makes us more flexible in operating between threads. Let's take a look at the internal mechanism by analyzing the source code. Condition is an interface, and the real implementation is the inner class ConditionObject in AbstractQueuedSynchronizer.
Basic attributes:
Public class ConditionObject implements Condition, java.io.Serializable {private static final long serialVersionUID = 1173984872572414699L; / * First node of condition queue. * / private transient Node firstWaiter; / * * Last node of condition queue. * / private transient Node lastWaiter;}
It can be seen from the basic properties that double-ended queues are maintained.
The await () method parses:
Public class ConditionObject implements Condition, java.io.Serializable {public final void await () throws InterruptedException {/ / 1. Determine whether the thread interrupts if (Thread.interrupted ()) {throw new InterruptedException ();} / / 2. Encapsulate the thread into a Node and put it in Condition Queue Node node = addConditionWaiter (); / / 3. Release all locks acquired by the current thread (PS: the current thread must have acquired an exclusive lock when calling the await method) int savedState = fullyRelease (node); int interruptMode = 0; / / 4. Determine whether the current thread is in SyncQueue (here there are two possibilities for Node transfer from Condtion Queue to SyncQueue / / (1) other threads call signal for transfer (2) the current thread is interrupted for Node transfer (transfer in checkInterruptWhileWaiting)) while (! isOnSyncQueue (node)) {/ / 5. If the current thread is not in Sync Queue, block LockSupport.park (this); / / 6. Determine whether the wake-up of the thread is due to the interruption of the thread. If it is interrupted, the node will be transferred in the transferAfterCancelledWait of checkInterruptWhileWaiting; if ((interruptMode = checkInterruptWhileWaiting (node))! = 0) {/ / indicates that it is awakened by thread interruption, and the node has been transferred to break in Sync Queue. }} / / 7. Call acquireQueued to acquire the exclusive lock in Sync Queue, and the return value indicates whether if (acquireQueued (node, savedState) & & interruptMode! = THROW_IE) {interruptMode = REINTERRUPT;} / / 8 has been interrupted during the acquisition. Determine whether the wake-up of a thread is interrupted or signal by "node.nextWaiter! = null". / / because if you wake up through an interrupt, the Node representing the thread will have if (node.nextWaiter! = null) {/ / 9 in both Condition Queue and Sync Queue. Clear the cancelled node unlinkCancelledWaiters ();} / 10. "interruptMode! = 0" means to wake up the thread if (interruptMode! = 0) {/ / 11 by interrupting. Depending on the type of interruptMode, decide whether to throw an exception or interrupt the reportInterruptAfterWait (interruptMode);}
The above source code shows that the queue maintained internally by Condition is a waiting queue. When the signal () method is called, the current thread node will be transferred from Condition queue to the Sync queue queue to compete for locks.
Signal () source code parsing:
Public class ConditionObject implements Condition, java.io.Serializable {public final void signal () {if (! isHeldExclusively ()) throw new IllegalMonitorStateException (); Node first = firstWaiter; if (first! = null) doSignal (first) } private void doSignal (Node first) {do {/ / the next node of the incoming linked list is empty, then the tail node leaves empty if ((firstWaiter = first.nextWaiter) = = null) lastWaiter = null; / / the next node of the current node is empty first.nextWaiter = null / / if you successfully convert node from condition queue to sync queue, exit the loop and exit the loop if the node is empty. Otherwise, find a node in the queue to wake up} while (! transferForSignal (first) & & (first = firstWaiter)! = null);}}
Signal () wakes up an arbitrary thread in the waiting queue, and signalAll () wakes up all threads in that queue. In this way, the directional function can be achieved by maintaining different threads through different queues. It can eliminate the resource loss caused by awakening prematurely. Note that you need to acquire the lock, that is, lock (), before using the signal () method, and then you need to unlock () as soon as possible to avoid the wear and tear of context switching.
Summary:
In the object-oriented world, a class often needs to complete the calculation with the help of other classes, and in the same world of threads, multiple threads can complete a task at the same time, and can better operate the thread by waking up and waiting. thus, the thread can allocate resources to it when it needs to use resources, and when it is not in use, it can give up resources to other threads. With regard to the Sync queue mentioned in Condition, you can refer to Java concurrency combined with source code analysis AQS principle to see how internally maintained queues acquire locks.
This is the end of "waiting and notification between Java concurrent threads". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.