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 ReentrantLockmetodo s' newCondition()restituisce la AbstractQueuedSynchronizer's implementazione di Conditioninterfaccia, annidato ConditionObject(a proposito, è l'unica implementazione di Conditionun'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 AbstractQueuedSynchronizercoinvolta l' implementazione basata su.
AbstractQueuedSynchronizerutilizza basso livello LockSupport's parke unparkmetodi, e se si richiama LockSupport.unparka 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 Conditionmetodi in attesa sono discussi nel javadoc Conditiondell'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 AbstractQueuedSynchronizerl'implementazione Conditiondell'interfaccia fa esattamente questo - rimuove qualsiasi possibilità di risvegli spuri .
Ciò vale sicuramente per ConditionObjecti metodi in attesa di altri .
Quindi, la conclusione è:
dovremmo sempre chiamare Condition.awaitnel 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 Conditiondell'interfaccia)
pthread_cond_wait()la vera domanda è "Perché pthread_cond_wait ha risvegli spuri?" .