Beantwortung der Frage des OP
Was kann ich tun, um dieses Warten fälschlicherweise aufzuwecken, ohne ewig auf ein zufälliges Ereignis zu warten?
, Keine jede falsche Wakeup könnte diese Erwartung Thread aufwachen!
Unabhängig davon , ob falsche Wakeups kann oder nicht auf einer bestimmten Plattform, in einem Fall , der OPs passieren kann Snippet es positiv ist unmöglich für Condition.await()
zurückzukehren und die Linie zu sehen „Unechte Wakeup!“ im Ausgabestream.
Es sei denn, Sie verwenden eine sehr exotische Java-Klassenbibliothek
Dies liegt daran , Standard, OpenJDK ‚s ReentrantLock
‘ s Methode newCondition()
kehrt die AbstractQueuedSynchronizer
‚s Umsetzung der Condition
Schnittstelle, verschachtelt ConditionObject
(übrigens ist es die einzige Implementierung der Condition
Schnittstelle in dieser Klassenbibliothek) und die ConditionObject
‘ s Methode await()
selbst überprüft , ob die Bedingung nicht hält und kein falsches Aufwecken könnte diese Methode zwingen, fälschlicherweise zurückzukehren.
Übrigens können Sie es selbst überprüfen, da es ziemlich einfach ist, ein falsches Aufwecken zu emulieren, sobald die AbstractQueuedSynchronizer
basierte Implementierung beteiligt ist.
AbstractQueuedSynchronizer
verwendet Low-Level LockSupport
‚s park
und unpark
Methoden, und wenn Sie invoke LockSupport.unpark
auf einem Thread wartet auf Condition
, kann diese Aktion nicht von einem falschen Wakeup unterschieden werden.
Das OP-Snippet leicht umgestalten,
public class Spurious {
private static class AwaitingThread extends Thread {
@Override
public void run() {
Lock lock = new ReentrantLock();
Condition cond = lock.newCondition();
lock.lock();
try {
try {
cond.await();
System.out.println("Spurious wakeup!");
} catch (InterruptedException ex) {
System.out.println("Just a regular interrupt.");
}
} finally {
lock.unlock();
}
}
}
private static final int AMOUNT_OF_SPURIOUS_WAKEUPS = 10;
public static void main(String[] args) throws InterruptedException {
Thread awaitingThread = new AwaitingThread();
awaitingThread.start();
Thread.sleep(10000);
for(int i =0 ; i < AMOUNT_OF_SPURIOUS_WAKEUPS; i++)
LockSupport.unpark(awaitingThread);
Thread.sleep(10000);
if (awaitingThread.isAlive())
System.out.println("Even after " + AMOUNT_OF_SPURIOUS_WAKEUPS + " \"spurious wakeups\" the Condition is stil awaiting");
else
System.out.println("You are using very unusual implementation of java.util.concurrent.locks.Condition");
}
}
Unabhängig davon, wie sehr der Unparking-Thread (Hauptthread) versuchen würde, den wartenden Thread zu aktivieren, wird die Condition.await()
Methode in diesem Fall niemals zurückkehren.
Die falschen Aufweckmethoden für Condition
die erwarteten Methoden werden im Javadoc der Condition
Schnittstelle erläutert . Obwohl es das sagt,
Wenn auf eine Bedingung gewartet wird, kann ein falsches Aufwecken auftreten
und das
Es wird empfohlen, dass Anwendungsprogrammierer immer davon ausgehen, dass sie auftreten können, und daher immer in einer Schleife warten.
aber es fügt das später hinzu
Eine Implementierung ist frei, um die Möglichkeit von falschen Aufwecken auszuschließen
und AbstractQueuedSynchronizer
die Implementierung der Condition
Schnittstelle macht genau das - beseitigt jede Möglichkeit von falschen Aufwecken .
Dies gilt sicherlich auch für ConditionObject
die erwarteten Methoden anderer .
Die Schlussfolgerung lautet also:
Wir sollten immer Condition.await
in der Schleife aufrufen und prüfen, ob die Bedingung nicht erfüllt ist, aber mit Standard, OpenJDK, Java Class Library kann dies niemals passieren . Es sei denn, Sie verwenden erneut eine sehr ungewöhnliche Java-Klassenbibliothek (was sehr, sehr ungewöhnlich sein muss, da andere bekannte Nicht-OpenJDK-Java-Klassenbibliotheken, derzeit fast ausgestorbene GNU-Klassenpfade und Apache Harmony , mit der Standardimplementierung der Condition
Schnittstelle identisch zu sein scheinen ).
pthread_cond_wait()
die eigentliche Frage verwenden, lautet die eigentliche Frage: "Warum hat pthread_cond_wait falsche Aufweckvorgänge?" .