Respondendo à pergunta do OP
O que posso fazer para despertar essa espuria sem esperar eternamente por um evento aleatório?
, nenhuma ativação espúria poderia ativar esse segmento em espera!
Independentemente de as ativações espúrias poderem ou não acontecer em uma plataforma específica, em um caso do snippet do OP, é positivamente impossível paraCondition.await() retornar e ver a linha "despertar espúrias!" no fluxo de saída.
A menos que você esteja usando uma Java Class Library muito exótica
Isso ocorre porque standard, OpenJDK 's ReentrantLockmétodo' s newCondition()retorna a AbstractQueuedSynchronizer'implementação s de Conditioninterface, aninhada ConditionObject(a propósito, é a única implementação da Conditioninterface, neste biblioteca de classes), eo ConditionObject' s método await()em si verifica se a condição não espera e nenhuma ativação espúria pode forçar esse método a retornar por engano.
A propósito, você pode verificar por si próprio, pois é muito fácil imitar a ativação espúria, uma vez que a AbstractQueuedSynchronizerimplementação baseada em está envolvida.
AbstractQueuedSynchronizerusa baixo nível LockSupport's parke unparkmétodos, e se você invocar LockSupport.unparkem um thread aguardando emCondition , esta ação não pode ser distinguido de um despertar espúria.
Refatorando levemente o snippet do 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");
}
}
, e não importa o quanto o encadeamento (principal) não-estacionário tentaria ativar o encadeamento em espera, o Condition.await()método nunca retornará nesse caso.
Os despertares espúrios Conditiondos métodos em espera são discutidos no javadoc da Conditioninterface . Embora diga isso,
ao esperar por uma Condição, é permitido que ocorra uma ativação espúria
e essa
é recomendável que os programadores de aplicativos sempre assumam que eles podem ocorrer e, portanto, sempre esperem em um loop.
mas depois acrescenta que
Uma implementação é gratuita para remover a possibilidade de despertares espúrios
e AbstractQueuedSynchronizera implementação da Conditioninterface faz exatamente isso - remove qualquer possibilidade de despertares espúrios .
Isso certamente vale para os outros ConditionObjectmétodos que aguardam.
Então, a conclusão é:
devemos sempre chamar Condition.awaitno loop e verificar se a condição não se mantém, mas com o OpenJDK padrão, o Java Class Library nunca pode acontecer . A menos que, novamente, você use Java Class Library muito incomum (o que deve ser muito incomum, porque outras bibliotecas de classes Java não OpenJDK conhecidas, atualmente quase extintas GNU Classpath e Apache Harmony , parecem ter idênticas à implementação padrão da Conditioninterface)
pthread_cond_wait()a pergunta real é "Por que pthread_cond_wait tem ativações espúrias?" .