Kann ich ein komplettes einfaches Szenario erhalten, dh ein Tutorial, das vorschlägt, wie dies verwendet werden soll, insbesondere mit einer Warteschlange?
Die Methoden wait()
und notify()
sollen einen Mechanismus bereitstellen, mit dem ein Thread blockieren kann, bis eine bestimmte Bedingung erfüllt ist. Ich gehe davon aus, dass Sie eine Implementierung für eine blockierende Warteschlange schreiben möchten, in der Sie einen Backing-Store mit Elementen fester Größe haben.
Das erste, was Sie tun müssen, ist, die Bedingungen zu identifizieren, auf die die Methoden warten sollen. In diesem Fall soll die put()
Methode blockiert werden, bis freier Speicherplatz im Speicher vorhanden ist, und die take()
Methode soll blockiert werden, bis ein Element zurückgegeben werden muss.
public class BlockingQueue<T> {
private Queue<T> queue = new LinkedList<T>();
private int capacity;
public BlockingQueue(int capacity) {
this.capacity = capacity;
}
public synchronized void put(T element) throws InterruptedException {
while(queue.size() == capacity) {
wait();
}
queue.add(element);
notify(); // notifyAll() for multiple producer/consumer threads
}
public synchronized T take() throws InterruptedException {
while(queue.isEmpty()) {
wait();
}
T item = queue.remove();
notify(); // notifyAll() for multiple producer/consumer threads
return item;
}
}
Es gibt einige Dinge zu beachten, wie Sie die Warte- und Benachrichtigungsmechanismen verwenden müssen.
Zunächst müssen Sie sicherstellen, dass alle Aufrufe an wait()
oder notify()
innerhalb eines synchronisierten Codebereichs (wobei die wait()
und notify()
-Aufrufe für dasselbe Objekt synchronisiert werden). Der Grund dafür (abgesehen von den Standardbedenken hinsichtlich der Thread-Sicherheit) liegt in einem sogenannten Fehlensignal.
Ein Beispiel hierfür ist, dass ein Thread möglicherweise aufruft, put()
wenn die Warteschlange voll ist. Er überprüft dann die Bedingung und stellt fest, dass die Warteschlange voll ist. Bevor er jedoch einen anderen Thread blockieren kann, ist geplant. Dieser zweite Thread ist dann take()
ein Element aus der Warteschlange und benachrichtigt die wartenden Threads, dass die Warteschlange nicht mehr voll ist. Da der erste Thread die Bedingung jedoch bereits überprüft hat, wird er wait()
nach dem erneuten Planen einfach aufgerufen , obwohl er Fortschritte erzielen könnte.
Durch die Synchronisierung auf einem freigegebenen Objekt können Sie sicherstellen, dass dieses Problem nicht auftritt, da der take()
Aufruf des zweiten Threads erst dann Fortschritte erzielen kann, wenn der erste Thread tatsächlich blockiert wurde.
Zweitens müssen Sie die Bedingung, die Sie überprüfen, in eine while-Schleife und nicht in eine if-Anweisung einfügen, da dies als falsches Aufwecken bezeichnet wird. Hier kann ein wartender Thread manchmal wieder aktiviert werden, ohne notify()
aufgerufen zu werden. Wenn Sie diese Prüfung in eine while-Schleife einfügen, wird sichergestellt, dass bei einem falschen Aufwecken die Bedingung erneut überprüft wird und der Thread wait()
erneut aufgerufen wird.
Wie einige der anderen Antworten bereits erwähnt haben, hat Java 1.5 eine neue Parallelitätsbibliothek (im java.util.concurrent
Paket) eingeführt, die eine übergeordnete Abstraktion über den Warte- / Benachrichtigungsmechanismus bietet. Mit diesen neuen Funktionen können Sie das ursprüngliche Beispiel folgendermaßen umschreiben:
public class BlockingQueue<T> {
private Queue<T> queue = new LinkedList<T>();
private int capacity;
private Lock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
public BlockingQueue(int capacity) {
this.capacity = capacity;
}
public void put(T element) throws InterruptedException {
lock.lock();
try {
while(queue.size() == capacity) {
notFull.await();
}
queue.add(element);
notEmpty.signal();
} finally {
lock.unlock();
}
}
public T take() throws InterruptedException {
lock.lock();
try {
while(queue.isEmpty()) {
notEmpty.await();
}
T item = queue.remove();
notFull.signal();
return item;
} finally {
lock.unlock();
}
}
}
Wenn Sie tatsächlich eine Blockierungswarteschlange benötigen, sollten Sie natürlich eine Implementierung der BlockingQueue- Schnittstelle verwenden.
Für solche Dinge würde ich Java Concurrency in der Praxis sehr empfehlen , da es alles abdeckt, was Sie über Probleme und Lösungen im Zusammenhang mit Parallelität wissen möchten.
notify
weckt nur einen Thread. Wenn zwei Consumer-Threads miteinander konkurrieren, um ein Element zu entfernen, weckt eine Benachrichtigung möglicherweise den anderen Consumer-Thread, der nichts dagegen tun kann und wieder in den Ruhezustand wechselt (anstelle des Produzenten, von dem wir gehofft hatten, dass er ein neues Element einfügt.) Weil Der Producer-Thread wird nicht geweckt, es wird nichts eingefügt und jetzt werden alle drei Threads auf unbestimmte Zeit schlafen. Ich entfernte meinen vorherigen Kommentar, als er (fälschlicherweise) sagte, dass falsches Aufwachen die Ursache des Problems war (ist es nicht.)Kein Warteschlangenbeispiel, aber extrem einfach :)
Einige wichtige Punkte:
1) NIEMALS tun
Immer verwenden während (Bedingung), weil
while(!pizzaExists){ wait(); }
.2) Sie müssen die Sperre (synchronisiert) gedrückt halten, bevor Sie wait / nofity aufrufen. Die Fäden müssen vor dem Aufwachen ebenfalls blockiert werden.
3) Vermeiden Sie es, eine Sperre innerhalb Ihres synchronisierten Blocks zu erlangen, und versuchen Sie, keine außerirdischen Methoden aufzurufen (Methoden, die Sie nicht genau kennen). Wenn Sie müssen, stellen Sie sicher, dass Sie Maßnahmen ergreifen, um Deadlocks zu vermeiden.
4) Seien Sie vorsichtig mit notify (). Bleiben Sie bei notifyAll (), bis Sie wissen, was Sie tun.
5) Lesen Sie zu guter Letzt Java Concurrency in der Praxis !
quelle
pizzaArrived
Flagge benutzen ? Wenn das Flag ohne Aufruf geändertnotify
wird, hat dies keine Auswirkung. Auch nur mitwait
undnotify
ruft das Beispiel auf.synchronized
Schlüsselwort geschützt ist , ist es redundant, die Variable zu deklarierenvolatile
, und es wird empfohlen, sie zu vermeiden, um Verwirrung zu vermeiden. @MridaAuch wenn Sie nach
wait()
undnotify()
speziell gefragt haben , halte ich dieses Zitat für wichtig genug:Josh Bloch, Effective Java 2nd Edition , Punkt 69: Bevorzugen Sie Parallelitätsdienstprogramme gegenüber
wait
undnotify
(Hervorhebung seiner):quelle
notify()
und das niewait()
wiederHaben Sie sich dieses Java-Tutorial angesehen ?
Außerdem würde ich Ihnen raten, sich nicht mit solchen Dingen in echter Software zu beschäftigen. Es ist gut, damit zu spielen, damit Sie wissen, was es ist, aber Parallelität hat überall Fallstricke. Es ist besser, übergeordnete Abstraktionen und synchronisierte Sammlungen oder JMS-Warteschlangen zu verwenden, wenn Sie Software für andere Personen erstellen.
Das ist zumindest was ich tue. Ich bin kein Experte für Parallelität, daher halte ich mich nach Möglichkeit davon fern, Threads von Hand zu behandeln.
quelle
Beispiel
quelle
Beispiel für wait () und notifyall () in Threading.
Eine synchronisierte statische Array-Liste wird als Ressource verwendet, und die wait () -Methode wird aufgerufen, wenn die Array-Liste leer ist. Die notify () -Methode wird aufgerufen, sobald ein Element für die Array-Liste hinzugefügt wird.
quelle
if(arrayList.size() == 0)
, ich denke, es könnte ein Fehler hier sein.