Respondiendo la pregunta del OP
¿Qué puedo hacer para despertar esto espurio sin esperar para siempre un evento aleatorio?
, ¡ ningún despertar espurio podría despertar este hilo en espera!
Independientemente de si los despertares espurios pueden o no suceder en una plataforma en particular, en el caso del fragmento del OP, es positivamente imposible paraCondition.await()
volver y ver la línea "despertar espurias!" en la secuencia de salida.
A menos que esté utilizando una biblioteca de clases Java muy exótica
Esto es porque estándar, OpenJDK 's ReentrantLock
método' s newCondition()
devuelve los AbstractQueuedSynchronizer
's de la aplicación de Condition
interfaz, anidado ConditionObject
(por cierto, es la única aplicación de Condition
interfaz de esta biblioteca de clases), y el ConditionObject
' método s await()
sí mismo comprueba si la condición no hace se mantiene y ningún despertar espurio podría obligar a este método a regresar por error.
Por cierto, puede verificarlo usted mismo, ya que es bastante fácil emular la activación espuria una vez que la AbstractQueuedSynchronizer
implementación basada está involucrada.
AbstractQueuedSynchronizer
usos de bajo nivel LockSupport
's park
y unpark
métodos, y si se invoca LockSupport.unpark
en un hilo en esperaCondition
, esta acción no puede distinguirse de un despertar espuria.
Refactorizando ligeramente el fragmento del 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");
}
}
, y no importa cuánto intente el hilo de estacionamiento (principal) intentar despertar el hilo en espera, el Condition.await()
método nunca volverá en este caso.
Los espurios despertares de Condition
los métodos de espera se discuten en el javadoc de la Condition
interfaz . Aunque sí dice eso,
cuando se espera una Condición, se permite que ocurra un despertar espurio
y eso
Se recomienda que los programadores de aplicaciones siempre asuman que pueden ocurrir y, por lo tanto, siempre esperen en un bucle.
pero luego agrega que
Una implementación es gratuita para eliminar la posibilidad de despertar espurios
y AbstractQueuedSynchronizer
la implementación de la Condition
interfaz hace exactamente eso: elimina cualquier posibilidad de reactivaciones espurias .
Esto seguramente es válido para ConditionObject
los métodos de espera de otros.
Entonces, la conclusión es:
siempre debemos llamar Condition.await
al bucle y verificar si la condición no se cumple, pero con OpenJDK estándar, Java Class Library nunca puede suceder . A menos que, de nuevo, utilice la Biblioteca de clases Java muy inusual (que debe ser muy muy inusual, porque otras Bibliotecas de clases Java no OpenJDK conocidas, actualmente GNU Classpath y Apache Harmony , casi extintas , parecen tener una implementación de Condition
interfaz estándar )
pthread_cond_wait()
la pregunta real es "¿Por qué pthread_cond_wait tiene despertadores espurios?" .