A ist wait()
nur dann sinnvoll, wenn es auch ein notify()
gibt. Es geht also immer um die Kommunikation zwischen Threads, und das muss synchronisiert werden, damit es richtig funktioniert. Man könnte argumentieren, dass dies implizit sein sollte, aber das würde aus folgendem Grund nicht wirklich helfen:
Semantisch gesehen nie einfach wait()
. Sie benötigen eine Bedingung, um zufrieden zu sein, und wenn dies nicht der Fall ist, warten Sie, bis dies der Fall ist. Was Sie also wirklich tun, ist
if(!condition){
wait();
}
Die Bedingung wird jedoch von einem separaten Thread festgelegt. Damit dies ordnungsgemäß funktioniert, müssen Sie eine Synchronisierung durchführen.
Noch ein paar Dinge, die daran falsch sind. Nur weil Ihr Thread aufhört zu warten, bedeutet dies nicht, dass die gesuchte Bedingung wahr ist:
Sie können falsche Weckrufe erhalten (was bedeutet, dass ein Thread vom Warten aufwachen kann, ohne jemals eine Benachrichtigung erhalten zu haben) oder
Die Bedingung kann festgelegt werden, aber ein dritter Thread macht die Bedingung erneut falsch, wenn der wartende Thread aufwacht (und den Monitor erneut abruft).
Um mit diesen Fällen fertig zu werden, brauchen Sie immer eine Variation davon:
synchronized(lock){
while(!condition){
lock.wait();
}
}
Besser noch, spielen Sie überhaupt nicht mit den Synchronisationsprimitiven und arbeiten Sie mit den in den java.util.concurrent
Paketen angebotenen Abstraktionen .
Thread.interrupted()
.Lassen Sie uns anhand
wait()
eines konkreten Beispiels veranschaulichen, auf welche Probleme wir stoßen würden, wenn sie außerhalb eines synchronisierten Blocks aufgerufen werden könnten .Angenommen, wir würden eine Blockierungswarteschlange implementieren (ich weiß, es gibt bereits eine in der API :)
Ein erster Versuch (ohne Synchronisation) könnte etwas in der folgenden Richtung aussehen
Dies könnte möglicherweise passieren:
Ein Consumer-Thread ruft auf
take()
und sieht, dass diebuffer.isEmpty()
.Bevor der Consumer-Thread weiter aufgerufen wird
wait()
, kommt ein Producer-Thread und ruft einen vollständigen Thread aufgive()
, d. H.buffer.add(data); notify();
Der Consumer-Thread ruft jetzt auf
wait()
(und übersieht dennotify()
gerade aufgerufenen).Wenn Sie Pech haben, wird der Producer-Thread nicht mehr produzieren
give()
, da der Consumer-Thread nie aufwacht und wir einen Deadlock haben.Sobald Sie das Problem verstanden haben, liegt die Lösung auf der Hand: Verwenden Sie
synchronized
diese Option , um sicherzustellen, dassnotify
niemals zwischenisEmpty
und aufgerufen wirdwait
.Ohne auf Details einzugehen: Dieses Synchronisationsproblem ist universell. Wie Michael Borgwardt betont, dreht sich beim Warten / Benachrichtigen alles um die Kommunikation zwischen Threads, sodass Sie immer eine ähnliche Rennbedingung haben wie oben beschrieben. Aus diesem Grund wird die Regel "Nur innerhalb synchronisiert warten" erzwungen.
Ein Absatz aus dem von @Willie geposteten Link fasst es recht gut zusammen:
Das Prädikat, auf das sich Hersteller und Verbraucher einigen müssen, ist im obigen Beispiel dargestellt
buffer.isEmpty()
. Die Vereinbarung wird gelöst, indem sichergestellt wird, dass das Warten und Benachrichtigen insynchronized
Blöcken ausgeführt wird.Dieser Beitrag wurde hier als Artikel umgeschrieben: Java: Warum warten muss in einem synchronisierten Block aufgerufen werden
quelle
return buffer.remove();
in while block aber danachwait();
funktioniert es?wait
Rückgabe leer sein .Thread.currentThread().wait();
in dermain
Funktion von Try-Catch für umgebenInterruptedException
. Ohnesynchronized
Block gibt es mir die gleiche AusnahmeIllegalMonitorStateException
. Was bringt es jetzt dazu, einen illegalen Staat zu erreichen? Es funktioniert jedoch innerhalb dessynchronized
Blocks.@ Rollerball ist richtig. Das
wait()
wird aufgerufen, damit der Thread warten kann, bis eine Bedingung auftritt, wenn dieserwait()
Aufruf auftritt. Der Thread ist gezwungen, seine Sperre aufzugeben.Um etwas aufzugeben, müssen Sie es zuerst besitzen. Der Thread muss zuerst das Schloss besitzen. Daher muss es innerhalb einer
synchronized
Methode / eines Blocks aufgerufen werden .Ja, ich stimme allen obigen Antworten in Bezug auf mögliche Schäden / Inkonsistenzen zu, wenn Sie den Zustand innerhalb der
synchronized
Methode / des Blocks nicht überprüft haben . Wie jedoch @ shrini1000 hervorgehoben hat, verhindert das Aufrufenwait()
innerhalb eines synchronisierten Blocks nicht, dass diese Inkonsistenz auftritt .Hier ist eine schöne Lektüre ..
quelle
Das Problem, das auftreten kann, wenn Sie vorher nicht synchronisieren,
wait()
ist wie folgt:makeChangeOnX()
die while-Bedingung geht und diese überprüft und es isttrue
(gibtx.metCondition()
zurückfalse
, bedeutetx.condition
istfalse
), wird es in ihn gelangen. Dann geht kurz vor derwait()
Methode ein anderer Thread zusetConditionToTrue()
und setzt dasx.condition
zutrue
undnotifyAll()
.wait()
Methode eingegeben (nicht betroffen von demnotifyAll()
, was einige Momente zuvor passiert ist). In diesem Fall wartet der erste Thread auf die Ausführung eines anderen Threads, diessetConditionToTrue()
kann jedoch möglicherweise nicht erneut auftreten.quelle
Wir alle wissen, dass die Methoden wait (), notify () und notifyAll () für die Kommunikation zwischen Threads verwendet werden. Um verpasste Signale und falsche Weckprobleme zu beseitigen, wartet der wartende Thread unter bestimmten Bedingungen immer. z.B-
Das Benachrichtigen von Thread-Sets war die Variable NotNotified auf true und notify.
Jeder Thread hat seinen lokalen Cache, sodass alle Änderungen zuerst dort geschrieben und dann schrittweise in den Hauptspeicher befördert werden.
Wenn diese Methoden nicht innerhalb des synchronisierten Blocks aufgerufen würden, würde die Variable wasNotified nicht in den Hauptspeicher geleert und würde sich im lokalen Cache des Threads befinden, sodass der wartende Thread weiterhin auf das Signal wartet, obwohl es durch Benachrichtigen des Threads zurückgesetzt wurde.
Um diese Art von Problemen zu beheben, werden diese Methoden immer innerhalb des synchronisierten Blocks aufgerufen, wodurch sichergestellt wird, dass beim Start des synchronisierten Blocks alles aus dem Hauptspeicher gelesen und vor dem Verlassen des synchronisierten Blocks in den Hauptspeicher geleert wird.
Danke, hoffe es klärt.
quelle
Dies hat im Wesentlichen mit der Hardwarearchitektur (dh RAM und Caches ) zu tun .
Wenn Sie nicht verwenden ,
synchronized
zusammen mitwait()
odernotify()
, ein anderer Thread könnte den gleichen Block eingeben , anstatt für den Monitor des Wartens , es zu betreten. Wenn darüber hinaus ohne einen synchronisierten Block ein Array zB zugreift, kann ein anderer Thread nicht changement dafür ... tatsächlich ein anderer Thread wird nicht irgendwelche Change dafür , wenn es bereits eine Kopie des Arrays in der x-Level - Cache hat ( aka (1st / 2nd / 3rd-Level-Caches) des Thread-Handling-CPU-Kerns.Synchronisierte Blöcke sind jedoch nur eine Seite der Medaille: Wenn Sie tatsächlich von einem nicht synchronisierten Kontext aus auf ein Objekt in einem synchronisierten Kontext zugreifen, wird das Objekt auch innerhalb eines synchronisierten Blocks nicht synchronisiert, da es eine eigene Kopie von enthält Objekt in seinem Cache. Ich habe hier über diese Probleme geschrieben: https://stackoverflow.com/a/21462631 und Wenn eine Sperre ein nicht endgültiges Objekt enthält, kann die Objektreferenz noch von einem anderen Thread geändert werden?
Darüber hinaus bin ich überzeugt, dass die x-Level-Caches für die meisten nicht reproduzierbaren Laufzeitfehler verantwortlich sind. Das liegt daran, dass die Entwickler in der Regel nicht lernen, wie die CPU funktioniert oder wie sich die Speicherhierarchie auf die Ausführung von Anwendungen auswirkt: http://en.wikipedia.org/wiki/Memory_hierarchy
Es bleibt ein Rätsel, warum Programmierklassen nicht zuerst mit der Speicherhierarchie und der CPU-Architektur beginnen. "Hallo Welt" wird hier nicht helfen. ;)
quelle
direkt aus diesem Java Orakel Tutorial:
quelle
Wenn Sie notify () von einem Objekt t aus aufrufen, benachrichtigt Java eine bestimmte t.wait () -Methode. Aber wie sucht und benachrichtigt Java eine bestimmte Wartemethode?
Java untersucht nur den synchronisierten Codeblock, der durch das Objekt t gesperrt wurde. Java kann nicht den gesamten Code durchsuchen, um ein bestimmtes t.wait () zu benachrichtigen.
quelle
gemäß Dokumentation:
wait()
Methode bedeutet einfach, dass die Sperre für das Objekt aufgehoben wird. Das Objekt wird also nur innerhalb des synchronisierten Blocks / der synchronisierten Methode gesperrt. Wenn sich der Thread außerhalb des Synchronisierungsblocks befindet, bedeutet dies, dass er nicht gesperrt ist. Wenn er nicht gesperrt ist, was würden Sie dann für das Objekt freigeben?quelle
Thread wartet auf das Überwachungsobjekt (Objekt, das vom Synchronisationsblock verwendet wird). Es kann n Anzahl von Überwachungsobjekten auf der gesamten Reise eines einzelnen Threads geben. Wenn der Thread außerhalb des Synchronisationsblocks wartet, gibt es kein Überwachungsobjekt und auch keinen anderen Thread, der für den Zugriff auf das Überwachungsobjekt benachrichtigt wird. Wie würde der Thread außerhalb des Synchronisationsblocks wissen, dass er benachrichtigt wurde? Dies ist auch einer der Gründe, warum wait (), notify () und notifyAll () eher in der Objektklasse als in der Thread-Klasse liegen.
Grundsätzlich ist das Überwachungsobjekt hier eine gemeinsame Ressource für alle Threads, und Überwachungsobjekte können nur im Synchronisationsblock verfügbar sein.
quelle