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 Condition
interfejsu, zagnieżdżone ConditionObject
(nawiasem mówiąc, jest to jedyna realizacja Condition
interfejsu 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 AbstractQueuedSynchronizer
zaangażowaniu opartej na implementacji.
AbstractQueuedSynchronizer
używa niskiego poziomu LockSupport
dydaktycznego park
i unpark
metod, a jeśli wywołasz LockSupport.unpark
na nitce w oczekiwaniu na Condition
dział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 Condition
oczekujących metod są omówione w javadoc Condition
interfejsu . 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 AbstractQueuedSynchronizer
implementacja Condition
interfejsu robi dokładnie to - eliminuje wszelkie możliwości fałszywych pobudek .
Z pewnością dotyczy to innych ConditionObject
oczekujących metod.
Tak więc wniosek jest:
zawsze powinniśmy wywoływać Condition.await
pę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ę Condition
interfejsu)
pthread_cond_wait()
prawdziwe pytanie brzmi: „Dlaczego pthread_cond_wait ma fałszywe budzenie?” .