Das ist falsch:
synchronized(foo) {
foo.wait();
}
Das Problem ist, was dieses Thema zu wecken ist los? Das heißt, wie garantieren Sie , dass der andere Thread nicht foo.notify()
vor der erste Thread ruft foo.wait()
aufrufen wird? Das ist wichtig, weil das foo-Objekt sich nicht daran erinnert, dass es benachrichtigt wurde, wenn der Benachrichtigungsaufruf zuerst stattfindet. Wenn es nur eine notify() gibt, und wenn dies vor der wait() geschieht, wird wait() niemals zurückkehren.
Hier ist, wie warten und notify gemeint waren verwendet werden:
private Queue<Product> q = ...;
private Object lock = new Object();
void produceSomething(...) {
Product p = reallyProduceSomething();
synchronized(lock) {
q.add(p);
lock.notify();
}
}
void consumeSomething(...) {
Product p = null;
synchronized(lock) {
while (q.peek() == null) {
lock.wait();
}
p = q.remove();
}
reallyConsume(p);
}
Die wichtigsten Dinge, die in diesem Beispiel zu beachten sind, dass es ein expliziter Test für den Zustand (dh q.peek() ! = null), und niemand ändert die Bedingung, ohne die Sperre zu sperren.
Wenn der Benutzer zuerst angerufen wird, wird die Warteschlange leer und es wird warten. Es gibt keinen Moment, an dem der Produzent hineinschlüpfen, ein Produkt zur Warteschlange hinzufügen und dann die Sperre mitteilen kann, bis der Verbraucher bereit ist, diese Benachrichtigung zu erhalten.
Auf der anderen Seite, wenn der Produzent zuerst angerufen wird, dann ist der Verbraucher garantiert nicht zu warten(). Die Schleife im Consumer ist aus zwei Gründen wichtig: Erstens, wenn es mehr als einen Consumer-Thread gibt, dann ist es möglich, dass ein Konsument eine Benachrichtigung erhält, aber dann schleicht sich ein anderer Konsument ein und stiehlt das Produkt aus der Warteschlange. Das einzig Vernünftige, was der erste Verbraucher in diesem Fall tun kann, ist wieder auf das nächste Produkt zu warten. Der andere Grund, warum die Schleife wichtig ist, ist, dass das Javadoc sagt, dass Object.wait() zurückgeben darf, selbst wenn das Objekt nicht benachrichtigt wurde. Das nennt man ein "unechtes Wakeup", und die richtige Art, damit umzugehen, ist es, zurück zu gehen und wieder zu warten.
Beachten Sie auch: Die Sperre ist private
und die Warteschlange ist private
. Dies garantiert, dass keine andere Übersetzungseinheit die Synchronisation in dieser Übersetzungseinheit stören wird.
Und Hinweis: Die Sperre ist ein anderes Objekt als die Warteschlange selbst. Dies garantiert, dass die Synchronisation in dieser Kompilierungseinheit nicht mit der Synchronisation kollidiert, die die Queue-Implementierung (falls vorhanden) ausführt.
HINWEIS: Mein Beispiel erfindet ein Rad neu, um einen Punkt zu beweisen. In realem Code würden Sie die put() - und take() - Methoden einer ArrayBlockingQueue verwenden, die sich um das Warten und Benachrichtigen für Sie kümmert.
Was genau versuchen Sie mit den Schlössern zu erreichen? – Krease
@Chris gibt exception.so ich muss sperren – Asthme
Ich verstehe, dass Sie synchronisiert verwenden müssen, wenn Sie 'wait' aufrufen, aber ich verstehe nicht (a), warum Sie auch beim Aufruf von' updateThread synchronisieren. start ', oder (b) warum Sie' wait' verwenden, um damit zu beginnen (da Sie nicht 'notify' oder' notifyAll' verwenden). Ich vermute, du synchronisierst und verwendest 'wait', wo du nicht sein solltest. – Krease