Odpowiedź na pytanie PO
Co mogę zrobić, aby obudzić to fałszywie oczekiwane, nie czekając wiecznie na przypadkowe wydarzenie?
, żadne fałszywe budzenie nie mogłoby obudzić tego oczekującego wątku!
Niezależnie od tego, czy fałszywych wybudzeń może lub nie może się zdarzyć na danej platformie, w przypadku OP snippet to pozytywnie niemożliwe do Condition.await()powrotu i zobaczyć linię „Spurious wzbudzenia!” w strumieniu wyjściowym.
Chyba że używasz bardzo egzotycznej biblioteki klas Java
To dlatego, że standard, openjdk „s ReentrantLock” s metoda newCondition()zwraca AbstractQueuedSynchronizer„s implementacja Conditioninterfejsu, zagnieżdżone ConditionObject(nawiasem mówiąc, jest to jedyna realizacja Conditioninterfejsu w tej bibliotece klasy), a ConditionObject” s metoda await()sam sprawdza, czy warunek ten nie wstrzymanie i żadne fałszywe wybudzenie nie zmusiłoby tej metody do błędnego powrotu.
Nawiasem mówiąc, możesz to sprawdzić sam, ponieważ bardzo łatwo jest emulować fałszywe wybudzanie po AbstractQueuedSynchronizerzaangażowaniu opartej na implementacji.
AbstractQueuedSynchronizerużywa niskiego poziomu LockSupportdydaktycznego parki unparkmetod, a jeśli wywołasz LockSupport.unparkna nitce w oczekiwaniu na Conditiondziałanie to nie można odróżnić od fałszywych wznawianiu.
Nieznacznie refaktoryzuje fragment OP,
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");
}
}
, i bez względu na to, jak mocno wątek unparking (główny) próbowałby obudzić oczekujący wątek, Condition.await()metoda nigdy nie powróci w tym przypadku.
Fałszywe pobudki Conditionoczekujących metod są omówione w javadoc Conditioninterfejsu . Chociaż tak mówi,
podczas oczekiwania na Warunek, może nastąpić fałszywy pobudka
i to
zaleca się, aby programiści aplikacji zawsze zakładali, że mogą się zdarzyć, i dlatego zawsze czekają w pętli.
ale później to dodaje
Implementacja jest darmowa, aby usunąć możliwość fałszywych pobudek
i AbstractQueuedSynchronizerimplementacja Conditioninterfejsu robi dokładnie to - eliminuje wszelkie możliwości fałszywych pobudek .
Z pewnością dotyczy to innych ConditionObjectoczekujących metod.
Tak więc wniosek jest:
zawsze powinniśmy wywoływać Condition.awaitpętlę i sprawdzać, czy warunek nie jest spełniony, ale przy standardowym OpenJDK biblioteka klas Java nigdy nie może się zdarzyć . Chyba że ponownie użyjesz bardzo nietypowej biblioteki klas Java (która musi być bardzo niezwykła, ponieważ inne dobrze znane biblioteki klas Java innej niż OpenJDK, obecnie prawie wymarłe GNU Classpath i Apache Harmony , wydają się mieć identyczną standardową implementację Conditioninterfejsu)
pthread_cond_wait()prawdziwe pytanie brzmi: „Dlaczego pthread_cond_wait ma fałszywe budzenie?” .