Heim >Java >javaLernprogramm >Detaillierte Erläuterung der Warte-/Benachrichtigungsinstanzen der Kommunikation zwischen Java-Threads
Der folgende Editor bietet Ihnen eine kurze Diskussion zum Warten/Benachrichtigen der Kommunikation zwischen Java-Threads. Der Herausgeber findet es ziemlich gut, deshalb teile ich es jetzt mit Ihnen und gebe es als Referenz. Folgen wir dem Editor und schauen wir uns
Wait/notify/notifyAll in Java an. Es handelt sich um eine Methode der Objektklasse sind alle native Methoden. Sie sind plattformabhängig und werden häufig zur Implementierung des Producer/Consumer-Musters verwendet. Schauen wir uns zunächst die relevanten Definitionen an:
wait(): Der Thread, der diese Methode aufruft, wechselt in den WATTING-Status und wartet nur auf Benachrichtigung oder Unterbrechung Von einem anderen Thread zurückkehren, nachdem die Methode wait() aufgerufen wurde, wird die Sperre des Objekts aufgehoben.
wait(long): Timeout wartet bis zu lange Millisekunden. Wenn keine Benachrichtigung erfolgt, kommt es zu einer Zeitüberschreitung und Rückkehr.
notify(): benachrichtigt einen Thread, der auf das Objekt wartet, über die Rückkehr von der Methode wait(), und die Voraussetzung für die Rückkehr ist, dass der Thread das Objekt erhält sperren.
notifyAll(): Alle Threads benachrichtigen, die auf dieses Objekt warten.
Ein kleines Beispiel
Wir haben unten ein kleines Knödelrestaurant und das Geschäft boomt ist Koch und Kellner im Laden. Um zu vermeiden, dass der Kellner jedes Mal, wenn er eine Portion zubereitet, eine Portion herausnimmt, was zu ineffizient ist und körperliche Energie verschwendet. Gehen Sie nun davon aus, dass der Kellner dem Kunden jedes Mal, wenn er 10 Portionen zubereitet, diese auf einem großen Holzteller serviert. Nachdem er jeden Tag 100 Portionen verkauft hat, schließt das Restaurant und der Koch und die Kellner gehen nach Hause, um sich auszuruhen.
Denken Sie darüber nach: Wenn Sie den Warte-/Benachrichtigungsmechanismus nicht verwenden, um diese Funktion zu implementieren, dann könnte der direkteste Weg darin bestehen, dass der Kellner ab und zu in die Küche geht und das herausnimmt Gerichte auf einem Teller nach 10 Portionen.
Diese Methode hat zwei große Nachteile:
1. Wenn der Kellner zu fleißig in die Küche geht, ist er zu müde. , Es ist besser, den Gästen jedes Mal eine Schüssel zu servieren, wenn Sie eine Schüssel zubereiten, und die Rolle des großen Holztellers wird nicht reflektiert. Die spezifische Manifestation auf der Ebene des Implementierungscodes besteht darin, dass eine kontinuierliche Schleife erforderlich ist und Prozessorressourcen verschwendet werden.
2. Wenn der Kellner nach längerer Zeit in die Küche geht, um nachzusehen, kann die Pünktlichkeit nicht garantiert werden. Möglicherweise hat der Koch bereits 10 Portionen zubereitet, aber der Kellner hat es nicht beachtet.
Für das obige Beispiel ist es viel sinnvoller, den Warte-/Benachrichtigungsmechanismus zu verwenden. Jedes Mal, wenn der Koch 10 Portionen zubereitet, ruft er „Die Knödel sind fertig und können mitgenommen werden.“ Als der Kellner die Benachrichtigung erhält, geht er in die Küche, um den Gästen die Knödel zu servieren. Der Koch hat noch nicht genug getan, d Er muss immer noch die Ohren spitzen und auf die Benachrichtigung des Kochs warten.
package ConcurrentTest; import thread.BlockQueue; /** * Created by chengxiao on 2017/6/17. */ public class JiaoziDemo { //创建个共享对象做监视器用 private static Object obj = new Object(); //大木盘子,一盘最多可盛10份饺子,厨师做满10份,服务员就可以端出去了。 private static Integer platter = 0; //卖出的饺子总量,卖够100份就打烊收工 private static Integer count = 0; /** * 厨师 */ static class Cook implements Runnable{ @Override public void run() { while(count<100){ synchronized (obj){ while (platter<10){ platter++; } //通知服务员饺子好了,可以端走了 obj.notify(); System.out.println(Thread.currentThread().getName()+"--饺子好啦,厨师休息会儿"); } try { //线程睡一会,帮助服务员线程抢到对象锁 Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+"--打烊收工,厨师回家"); } } /** * 服务员 */ static class Waiter implements Runnable{ @Override public void run() { while(count<100){ synchronized (obj){ //厨师做够10份了,就可以端出去了 while(platter < 10){ try { System.out.println(Thread.currentThread().getName()+"--饺子还没好,等待厨师通知..."); obj.wait(); BlockQueue } catch (InterruptedException e) { e.printStackTrace(); } } //饺子端给客人了,盘子清空 platter-=10; //又卖出去10份。 count+=10; System.out.println(Thread.currentThread().getName()+"--服务员把饺子端给客人了"); } } System.out.println(Thread.currentThread().getName()+"--打烊收工,服务员回家"); } } public static void main(String []args){ Thread cookThread = new Thread(new Cook(),"cookThread"); Thread waiterThread = new Thread(new Waiter(),"waiterThread"); cookThread.start(); waiterThread.start(); } }
Betriebsergebnisse
cookThread--饺子好啦,厨师休息会儿 waiterThread--服务员把饺子端给客人了 waiterThread--饺子还没好,等待厨师通知... cookThread--饺子好啦,厨师休息会儿 waiterThread--服务员把饺子端给客人了 waiterThread--饺子还没好,等待厨师通知... cookThread--饺子好啦,厨师休息会儿 waiterThread--服务员把饺子端给客人了 waiterThread--饺子还没好,等待厨师通知... cookThread--饺子好啦,厨师休息会儿 waiterThread--服务员把饺子端给客人了 waiterThread--饺子还没好,等待厨师通知... cookThread--饺子好啦,厨师休息会儿 waiterThread--服务员把饺子端给客人了 waiterThread--饺子还没好,等待厨师通知... cookThread--饺子好啦,厨师休息会儿 waiterThread--服务员把饺子端给客人了 waiterThread--饺子还没好,等待厨师通知... cookThread--饺子好啦,厨师休息会儿 waiterThread--服务员把饺子端给客人了 waiterThread--饺子还没好,等待厨师通知... cookThread--饺子好啦,厨师休息会儿 waiterThread--服务员把饺子端给客人了 waiterThread--饺子还没好,等待厨师通知... cookThread--饺子好啦,厨师休息会儿 waiterThread--服务员把饺子端给客人了 waiterThread--饺子还没好,等待厨师通知... cookThread--饺子好啦,厨师休息会儿 waiterThread--服务员把饺子端给客人了 waiterThread--打烊收工,服务员回家 cookThread--打烊收工,厨师回家
Betriebsmechanismus
Entlehnt von „Concurrency“. Ein Bild aus „The Art of Programming“, um den Funktionsmechanismus von Warten/Benachrichtigen zu verstehen
Einige Leute könnten interessiert sein Der sogenannte Überwachungsmonitor (Monitor) und die Objektsperre (Sperre) sind nicht gut verstanden. Hier ist eine einfache Erklärung:
jvm ordnet jedem Objekt und jeder Klasse eine Sperre zu ein Objekt. Es dient dazu, den mit dem Objekt verknüpften Monitor abzurufen.
Der Monitor kann nur dann abgerufen werden, wenn die Sperre abgerufen wird. Wenn die Objektsperre erfolgreich abgerufen wird, kann der Thread ebenfalls verwendet werden Um das Warten auf dem Server zu überwachen, wird die Sperre freigegeben und in die Warteschlange eingetragen.
Zum Unterschied zwischen Schlössern und Monitoren gibt es einen Artikel, der sehr ausführlich und ausführlich ist. Ich werde ihn hier zitieren. Lassen Sie uns über die Unterschiede zwischen Schlössern und Monitoren sprechen detail_Java-Parallelität
Lassen Sie uns gemäß der obigen Abbildung den spezifischen Prozess regeln
1. Zuerst erhält waitThread die Objektsperre , und ruft dann die Methode wait() auf. Zu diesem Zeitpunkt gibt der Wartethread die Objektsperre auf und tritt in die Warteschlange des Objekts ein. WaitQueue;
2 ruft die notify()-Methode auf. Zu diesem Zeitpunkt wird der wartende Thread waitThread von der Warteschlange WaitQueue in die synchronisierte Warteschlange SynchronizedQueue verschoben und der waitThread wechselt vom Wartezustand in den blockierten Zustand. Es ist zu beachten, dass notifyThread die Sperre zu diesem Zeitpunkt nicht sofort aufhebt. Es wird die Sperre erst dann aufheben, wenn die verbleibende Arbeit abgeschlossen ist.
3 Die Objektsperre von der Methode „wait()“ kehrt zurück, um nachfolgende Vorgänge fortzusetzen.
4 Ein auf dem Warte-/Benachrichtigungsmechanismus basierender Inter-Thread-Kommunikationsprozess endet.
Wie bei notifyAll werden im zweiten Schritt alle Threads in der Warteschlange in die Synchronisationswarteschlange verschoben.
Fallstricke vermeiden
Bei der Verwendung von wait/notify/notifyAll gibt es einige Besonderheiten. Hier ist eine Zusammenfassung:
1. /notify()/notifyAll(), was bedeutet, dass die Sperre zuerst erlangt werden muss. Wir haben dies bereits erwähnt, da der Monitor erst erhalten werden kann, nachdem die Sperre gesperrt wurde. Andernfalls löst JVM auch eine IllegalMonitorStateException aus.
2. Wenn Sie wait() verwenden, muss die Bedingung, um zu bestimmen, ob der Thread in den Wartezustand wechselt, while anstelle von if verwenden, da der wartende Thread möglicherweise versehentlich aktiviert wird. Daher sollten Sie while verwenden SchleifeÜberprüfen Sie vor und nach dem Warten, ob die Weckbedingungen erfüllt sind, um die Sicherheit zu gewährleisten.
3. Nachdem die Methode notify() oder notifyAll() aufgerufen wurde, gibt der Thread die Sperre nicht sofort frei. Der Aufruf verschiebt nur den wartenden Thread aus der Warteschlange in die Synchronisationswarteschlange, das heißt, der Thread-Status ändert sich von „Warten“ zu „Blockiert“. Der Thread erhält die Sperre des aufrufenden Objekts zurück.
PostscriptDies ist die Einführung von Warte-/Benachrichtigungsinhalten. Bei der tatsächlichen Verwendung sollte besonderes Augenmerk auf das oben genannte A gelegt werden Einige Punkte, aber im Allgemeinen verwenden wir wait/notify/notifyAll direkt, um die Kommunikation zwischen Threads abzuschließen. Es gibt nicht viele Möglichkeiten für das Producer/Consumer-Modell, da das Java-Parallelitätspaket viele hervorragende und exquisite Tools bereitgestellt hat, wie z BlockingQueue usw. werden später bei Gelegenheit ausführlich vorgestellt.
Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der Warte-/Benachrichtigungsinstanzen der Kommunikation zwischen Java-Threads. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!