wake-up-neo.com

Konzept hinter dem Setzen von wait (), notify () -Methoden in der Objektklasse

Es fällt mir schwer, das Konzept hinter wait() in der Object-Klasse zu verstehen. Berücksichtigen Sie für diese Fragen, ob wait() und notifyAll() in der Thread-Klasse sind.

class Reader extends Thread {
    Calculator c;
    public Reader(Calculator calc) {
        c = calc;
    }

    public void run() {
        synchronized(c) {                              //line 9
        try {
            System.out.println("Waiting for calculation...");
            c.wait();
        } catch (InterruptedException e) {}
            System.out.println("Total is: " + c.total);
        }
    }

    public static void main(String [] args) {
        Calculator calculator = new Calculator();
        new Reader(calculator).start();
        new Reader(calculator).start();
        new Reader(calculator).start();
        calculator.start();
    }
}

class Calculator extends Thread {
    int total;
    public void run() {
        synchronized(this) {                     //Line 31
            for(int i=0;i<100;i++) {
                total += i;
            }
             notifyAll();
        }
    } 
}

Meine Frage ist, welchen Unterschied es hätte machen können? In Zeile 9 erhalten wir eine Sperre für Objekt c und führen dann eine Wartezeit aus, die die Bedingung für das Warten erfüllt, dass wir eine Sperre für das Objekt erhalten müssen, bevor wir warten. Dies ist der Fall für notifyAll. Wir haben eine Sperre für das Objekt des Rechners in Zeile 31 erhalten .

40
Sunny

Es fällt mir schwer, das Konzept hinter dem Setzen von wait () in der Objektklasse zu verstehen. Für diese Fragen sollten Sie bedenken, dass sich wait () und notifyAll () in der Thread-Klasse befinden

In der Java-Sprache haben Sie wait() für eine bestimmte Instanz einer Object - einen Monitor, der genau diesem Objekt zugewiesen ist. Wenn Sie ein Signal an einen Thread senden möchten, der auf diese bestimmte Objektinstanz wartet, rufen Sie notify() für dieses Objekt auf. Wenn Sie ein Signal an alle Threads senden möchten, die auf diese Objektinstanz warten, verwenden Sie notifyAll() für dieses Objekt.

Wenn wait() und notify() stattdessen auf der Thread vorhanden wären, müsste jeder Thread den Status jedes anderen Threads kennen. Wie würde Thread1 wissen, dass Thread2 auf den Zugriff auf eine bestimmte Ressource wartete? Wenn thread1 thread2.notify() aufrufen muss, müsste es irgendwie herausfinden, dass thread2 auf ihn wartet. Es müsste einen Mechanismus für Threads geben, um die Ressourcen oder Aktionen zu registrieren, die sie benötigen, damit andere ihnen signalisieren können, wann etwas bereit oder verfügbar ist.

In Java ist das Objekt selbst die Entität, die von Threads gemeinsam genutzt wird, wodurch sie miteinander kommunizieren können. Die Threads haben keine spezifischen Kenntnisse voneinander und können asynchron laufen. Sie laufen und sperren, warten und benachrichtigen das Objekt , auf das sie zugreifen möchten. Sie haben keine Kenntnis von anderen Threads und müssen ihren Status nicht kennen. Sie müssen nicht wissen, dass es Thread2 ist, der auf die Ressource wartet. Sie benachrichtigen nur die Ressource, und wer wartet, wird gewartet (wenn jemand wartet).

In Java verwenden wir dann Sperrobjekte als Synchronisations-, Mutex- und Kommunikationspunkte zwischen Threads. Wir synchronisieren ein Sperrobjekt, um den Mutex-Zugriff auf einen wichtigen Codeblock zu erhalten und den Speicher zu synchronisieren. Wir warten auf ein Objekt, wenn wir darauf warten, dass sich eine Bedingung ändert - eine Ressource wird verfügbar. Wir benachrichtigen ein Objekt, wenn wir schlafende Fäden aufwecken möchten.

// locks should be final objects so the object instance we are synchronizing on,
// never changes
private final Object lock = new Object();
...
// ensure that the thread has a mutex lock on some key code
synchronized (lock) {
    ...
    // i need to wait for other threads to finish with some resource
    // this releases the lock and waits on the associated monitor
    lock.wait();
    ...
    // i need to signal another thread that some state has changed and they can
    // awake and continue to run
    lock.notify();
}

Ihr Programm kann eine beliebige Anzahl von Sperrobjekten enthalten - jedes Sperren einer bestimmten Ressource oder eines bestimmten Codesegments. Möglicherweise haben Sie 100 Sperrobjekte und nur 4 Threads. Wenn die Threads die verschiedenen Teile des Programms ausführen, erhalten sie exklusiven Zugriff auf eines der Sperrobjekte. Sie müssen auch nicht den laufenden Status der anderen Threads kennen.

Auf diese Weise können Sie die Anzahl der Threads, die in Ihrer Software ausgeführt werden, beliebig vergrößern oder verkleinern. Sie stellen fest, dass die 4 Threads die externen Ressourcen zu stark blockieren. Dann können Sie die Anzahl erhöhen. Wenn Sie Ihren angegriffenen Server zu stark drücken, reduzieren Sie die Anzahl der laufenden Threads. Die Sperrobjekte gewährleisten den Mutex und die Kommunikation zwischen den Threads, unabhängig davon, wie viele Threads ausgeführt werden.

92
Gray

Zum besseren Verständnis, warum die Methode wait () und notify () zur Objektklasse gehört, gebe ich Ihnen ein reales Beispiel: Angenommen, eine Tankstelle hat eine einzige Toilette, deren Schlüssel am Service Desk aufbewahrt wird. Die Toilette ist eine gemeinsame Ressource für Autofahrer. Um diese gemeinsam genutzte Ressource verwenden zu können, muss der potenzielle Benutzer einen Schlüssel für die Sperre der Toilette erwerben. Der Benutzer geht zum Service Desk und holt den Schlüssel ab, öffnet die Tür, schließt sie von innen ab und nutzt die Einrichtungen.

Kommt ein zweiter potenzieller Benutzer an der Tankstelle an, findet er die Toilette als geschlossen und daher für ihn nicht verfügbar. Er geht zum Service Desk, aber der Schlüssel ist nicht vorhanden, da er sich in der Hand des aktuellen Benutzers befindet. Wenn der aktuelle Benutzer fertig ist, öffnet er die Tür und gibt den Schlüssel an das Service Desk zurück. Er kümmert sich nicht darum, Kunden zu warten. Der Service Desk gibt dem wartenden Kunden den Schlüssel. Wenn mehrere potenzielle Benutzer auftauchen, während die Toilette gesperrt ist, müssen sie eine Warteschlange bilden, die auf den Schlüssel für die Sperre wartet. Jeder Thread hat keine Ahnung, wer auf der Toilette ist.

Bei der Anwendung dieser Analogie auf Java ist ein Java-Thread offensichtlich ein Benutzer, und die Toilette ist ein Codeblock, den der Thread ausführen möchte. Java bietet eine Möglichkeit, den Code für einen Thread zu sperren, der gerade mit dem synchronisierten Schlüsselwort ausgeführt wird, und andere Threads, die ihn verwenden möchten, warten zu lassen, bis der erste Thread abgeschlossen ist. Diese anderen Threads werden in den Wartezustand versetzt. Java ist NICHT AS FAIR als Servicestation, da keine Warteschlange für wartende Threads vorhanden ist. Jeder der wartenden Threads erhält möglicherweise als Nächstes den Monitor, unabhängig von der Reihenfolge, nach der er gefragt wurde. Die einzige Garantie ist, dass alle Threads früher oder später den überwachten Code verwenden können.

Zum Schluss noch die Antwort auf Ihre Frage: Die Sperre könnte das Schlüsselobjekt oder das Service Desk sein. Keiner davon ist ein Thread.

Dies sind jedoch die Objekte, die derzeit entscheiden, ob die Toilette verschlossen oder geöffnet ist. Dies sind die Objekte, die in der Lage sind, zu melden, dass das Badezimmer geöffnet ist ("Benachrichtigen"), oder die Personen bitten, zu warten, wenn sie gesperrt sind.

35
Roushan

Die anderen Antworten auf diese Frage übersehen alle den entscheidenden Punkt, dass in Java ein Mutex mit dem Objekt every verknüpft ist. (Ich gehe davon aus, dass Sie wissen, was ein Mutex oder eine "Sperre" ist.) Dies ist nicht der Fall in den meisten Programmiersprachen, die das Konzept von "Sperren" haben. In Ruby müssen Sie beispielsweise explizit so viele Mutex Objekte erstellen, wie Sie benötigen.

Ich glaube zu wissen, warum die Macher von Java diese Entscheidung getroffen haben (obwohl es meiner Meinung nach ein Fehler war). Der Grund liegt in der Aufnahme des Schlüsselworts synchronized. Ich glaube, dass die Macher von Java (naiv) dachten, dass es durch die Einbeziehung von synchronized -Methoden in die Sprache den Leuten leicht fallen würde, korrekten Multithread-Code zu schreiben - kapseln Sie einfach Ihren gesamten gemeinsamen Zustand in Objekte. Deklarieren Sie die Methoden, die auf diesen Status zugreifen, als synchronized, und Sie sind fertig! Aber es hat nicht so geklappt ...

Wie auch immer, da jede Klasse synchronized Methoden haben kann, muss es für jedes Objekt einen Mutex geben, den die synchronized Methoden sperren und entsperren können.

wait und notify sind beide auf Mutexe angewiesen. Vielleicht verstehen Sie bereits, warum dies der Fall ist ... Wenn nicht, kann ich weitere Erklärungen hinzufügen, aber jetzt wollen wir nur sagen, dass beide Methoden an einem Mutex arbeiten müssen. Jedes Java - Objekt hat einen Mutex. Daher ist es sinnvoll, dass wait und notify für jedes Java - Objekt aufgerufen werden können. Das bedeutet, dass sie als Methoden von Object deklariert werden müssen.

Eine andere Möglichkeit wäre gewesen, statische Methoden auf Thread zu setzen oder so etwas, das jedes Object als Argument nehmen würde. Das wäre für neue Java Programmierer viel weniger verwirrend gewesen. Aber sie haben es nicht so gemacht. Es ist viel zu spät, um eine dieser Entscheidungen zu ändern. schade!

6
Alex D

Antwort auf Ihre erste Frage: Da jedes Objekt in Java nur eine lock(monitor) undwait(),notify(),notifyAll() für die gemeinsame Nutzung des Monitors verwendet, sind sie Teil der Object-Klasse und nicht Threadclass.

4
amicngh

In einfachen Worten sind die Gründe wie folgt.

  1. Object hat Monitore.
  2. Mehrere Threads können auf eine Object zugreifen. Es kann immer nur ein Thread den Objektmonitor für synchronized Methoden/Blöcke halten.
  3. Die wait(), notify() and notifyAll()-Methode in der Object-Klasse ermöglicht es allen Threads, die auf dieser object erstellt wurden, mit anderen zu kommunizieren
  4. Sperren (mit synchronized or Lock API) und Kommunikation (wait() and notify()) sind zwei verschiedene Konzepte. 

Wenn die Thread-Klasse wait(), notify() and notifyAll()-Methoden enthält, werden folgende Probleme erzeugt:

  1. Thread Kommunikationsproblem
  2. Synchronization am Objekt ist nicht möglich. Wenn für jeden Thread ein Monitor vorhanden ist, haben wir keine Möglichkeit, eine Synchronisierung durchzuführen
  3. Inconsistency im Objektzustand 

Weitere Informationen finden Sie in diesem Artikel

1
Ravindra babu

wait- und Benachrichtigungsoperationen arbeiten für implizite Sperre, und implizite Sperre ermöglicht Inter-Thread-Kommunikation. Und alle Objekte haben eine eigene Kopie eines impliziten Objekts. Daher ist es eine gute Entscheidung, zu warten und zu benachrichtigen, wo implizite Sperren leben. 

Alternativ könnten Wait and Notify auch in der Thread-Klasse gelebt haben. Anstelle von wait () müssen wir möglicherweise Thread.getCurrentThread (). wait () aufrufen, genauso wie notify. Für Warte- und Benachrichtigungsoperationen gibt es zwei erforderliche Parameter. Einer ist der Thread, der wartet oder andere benachrichtigt, die implizite Sperre des Objekts. beide sind diese sowohl in Object als auch in der Thread-Klasse verfügbar. Die wait () -Methode in der Thread-Klasse hätte das gleiche getan wie in der Object-Klasse. Der aktuelle Thread würde in den Wartezustand versetzt.

Ja, ich denke, Wait and Notify hätte auch in der Thread-Klasse sein können, aber es ist eher eine Designentscheidung, die es in der Objektklasse hält.

0
leoismyname

Diese Methoden funktionieren für die Sperren und Sperren sind mit Object und nicht mit Threads verknüpft. Daher ist es in der Objektklasse.

Die Methoden wait (), notify () und notifyAll () sind nicht nur Methoden, sondern Synchronisationshilfsprogramme und werden im Kommunikationsmechanismus zwischen Threads in Java verwendet.

Weitere Informationen finden Sie unter: http://parameshk.blogspot.in/2013/11/why-wait-notify-und-notifyall-methods.html

0

Dies ist nur meine 2 Cent in dieser Frage ... nicht sicher, ob dies in seiner Gesamtheit gilt. 

Jedes Objekt verfügt über einen Monitor und eine Warteset -> Gruppe von Threads (wahrscheinlich mehr auf Betriebssystemebene). Dies bedeutet, dass der Monitor und das Warteset als private Mitglieder eines Objekts betrachtet werden können. Wenn die Methoden wait () und notify () in der Thread-Klasse vorhanden sind, bedeutet dies, den öffentlichen Zugriff auf das waitset zu gewähren oder get-set-Methoden zu verwenden, um das waitset zu ändern. Sie würden das nicht tun wollen, weil das Design schlecht ist.

Nun, da das Objekt den Thread bzw. die Threads kennt, die auf seinen Monitor warten, sollte es die Aufgabe des Objekts sein, die Threads, die auf ihn warten, zu wecken, anstatt ein Objekt der Thread-Klasse zu erhalten, das jeden von ihnen (was wäre nur möglich, wenn das Thread-Klassenobjekt Zugriff auf das Waitset erhält). Es ist jedoch nicht die Aufgabe eines bestimmten Threads, jeden der wartenden Threads zu aktivieren. (Dies ist genau das, was passieren würde, wenn alle diese Methoden in der Thread-Klasse wären). Seine Aufgabe ist es, das Schloss zu lösen und mit seiner eigenen Aufgabe voranzukommen. Ein Thread arbeitet unabhängig und muss nicht wissen, ob andere Threads auf den Objektmonitor warten (dies ist ein unnötiges Detail für das Thread-Klassenobjekt). Wenn es begann, jeden Thread für sich zu wecken, entfernt er seine Kernfunktionalität und führt seine eigene Aufgabe aus. Wenn Sie über eine Szene nachdenken, in der es möglicherweise Tausende von Threads gibt, können Sie davon ausgehen, wie stark die Auswirkungen auf die Leistung sein können. Vorausgesetzt, dass die Objektklasse weiß, wer darauf wartet, kann sie den Job ausführen, der die wartenden Threads aufweckt, und der Thread, der notify () gesendet hat, kann seine weitere Verarbeitung ausführen. 

Um eine Analogie zu geben (vielleicht nicht die richtige, aber kann an nichts anderes denken). Bei einem Stromausfall rufen wir einen Kundenvertreter dieses Unternehmens an, weil sie die richtigen Ansprechpartner für die Behebung des Problems kennt. Sie als Verbraucher dürfen nicht wissen, wer die Ingenieure dahinter sind, und selbst wenn Sie wissen, können Sie unmöglich jeden von ihnen anrufen und ihnen Ihre Probleme mitteilen (das ist nicht Ihre Pflicht. Ihre Pflicht besteht darin, sie darüber zu informieren Ausfall und die Aufgabe des CR ist es, die richtigen Ingenieure dafür zu informieren.

Lassen Sie mich wissen, ob dies richtig klingt ... (Ich habe die Fähigkeit, manchmal mit meinen Worten zu verwechseln).

0
BHS

die wait-wait-Methode weist den aktuellen Thread an, den Monitor aufzugeben und in den Ruhezustand zu wechseln.

benachrichtigen - Aktiviert einen einzelnen Thread, der auf dem Monitor dieses Objekts wartet.

Sie sehen also wait () - und notify () -Methoden auf Monitorebene arbeiten. Thread, der den Monitor gerade hält, wird aufgefordert, diesen Monitor durch die wait () - Methode und durch Benachrichtigungsmethode (oder notifyAll) -Threads, die auf den warten, aufzugeben Der Monitor des Objekts wird benachrichtigt, dass Threads aktiviert werden können.

Beachten Sie hierbei, dass der Monitor einem Objekt und nicht einem bestimmten Thread zugewiesen ist. Dies ist ein Grund, warum sich diese Methoden in der Object-Klasse befinden .. Um Threads zu wiederholen, warten Sie auf den Monitor eines Objekts (lock) und notify () wird auch für ein Objekt aufgerufen, um einen Thread zu aktivieren, der auf dem Monitor des Objekts wartet.

0
Ankur Kunal