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 ReentrantLock
método' s newCondition()
retorna a AbstractQueuedSynchronizer
'implementação s de Condition
interface, aninhada ConditionObject
(a propósito, é a única implementação da Condition
interface, 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 AbstractQueuedSynchronizer
implementação baseada em está envolvida.
AbstractQueuedSynchronizer
usa baixo nível LockSupport
's park
e unpark
métodos, e se você invocar LockSupport.unpark
em 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 Condition
dos métodos em espera são discutidos no javadoc da Condition
interface . 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 AbstractQueuedSynchronizer
a implementação da Condition
interface faz exatamente isso - remove qualquer possibilidade de despertares espúrios .
Isso certamente vale para os outros ConditionObject
métodos que aguardam.
Então, a conclusão é:
devemos sempre chamar Condition.await
no 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 Condition
interface)
pthread_cond_wait()
a pergunta real é "Por que pthread_cond_wait tem ativações espúrias?" .