Rispondere alla domanda del PO
Cosa posso fare per svegliare questa attesa spuria senza aspettare per sempre un evento casuale?
, nessun risveglio spurio potrebbe svegliare questo thread in attesa!
Indipendentemente dal fatto che wakeups spuri possono o non possono accadere su una particolare piattaforma, in un caso di del PO snippet è positivamente impossibile per Condition.await()
tornare e vedere la linea "Wakeup spurie!" nel flusso di output.
A meno che non si stia utilizzando una libreria di classi Java molto esotica
Questo perché di serie, OpenJDK 's ReentrantLock
metodo s' newCondition()
restituisce la AbstractQueuedSynchronizer
's implementazione di Condition
interfaccia, annidato ConditionObject
(a proposito, è l'unica implementazione di Condition
un'interfaccia in questa libreria di classi), e la ConditionObject
' s metodo await()
si verifica se la condizione non trattiene e nessun risveglio spurio potrebbe costringere questo metodo a restituire erroneamente.
A proposito, potresti controllarlo da solo poiché è abbastanza facile emulare il risveglio spurio una volta AbstractQueuedSynchronizer
coinvolta l' implementazione basata su.
AbstractQueuedSynchronizer
utilizza basso livello LockSupport
's park
e unpark
metodi, e se si richiama LockSupport.unpark
a un filo in attesa su Condition
, questa azione non può essere distinto da un risveglio spuria.
Rifattorizzare leggermente lo snippet del PO,
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");
}
}
e non importa quanto il thread unparking (principale) provi a risvegliare il thread in attesa, Condition.await()
in questo caso il metodo non ritornerà mai.
I risvegli spuri sui Condition
metodi in attesa sono discussi nel javadoc Condition
dell'interfaccia . Anche se lo dice,
quando si attende una Condizione, si può verificare un risveglio spurio
e quello
si consiglia ai programmatori di applicazioni di assumere sempre che possano verificarsi e quindi attendere sempre in un ciclo.
ma in seguito lo aggiunge
Un'implementazione è gratuita per rimuovere la possibilità di risvegli spuri
e AbstractQueuedSynchronizer
l'implementazione Condition
dell'interfaccia fa esattamente questo - rimuove qualsiasi possibilità di risvegli spuri .
Ciò vale sicuramente per ConditionObject
i metodi in attesa di altri .
Quindi, la conclusione è:
dovremmo sempre chiamare Condition.await
nel loop e verificare se la condizione non è valida, ma con OpenJDK standard, la libreria di classi Java non può mai accadere . A meno che, ancora una volta, usi una libreria di classi Java molto insolita (che deve essere molto insolita, perché un'altra libreria di classi Java non OpenJDK nota, attualmente quasi estinta GNU Classpath e Apache Harmony , sembra avere identica implementazione standard Condition
dell'interfaccia)
pthread_cond_wait()
la vera domanda è "Perché pthread_cond_wait ha risvegli spuri?" .