Répondre à la question du PO
Que puis-je faire pour réveiller cette attente faussement sans attendre indéfiniment un événement aléatoire?
, aucun réveil parasite ne pourrait réveiller ce fil en attente!
Indépendamment du fait que des réveils parasites peuvent ou non se produire sur une plate-forme particulière, dans le cas de l'extrait de l'OP, il est absolument impossible pourCondition.await()
revenir et de voir la ligne « Spurious réveil! » dans le flux de sortie.
Sauf si vous utilisez une bibliothèque de classes Java très exotique
En effet, la méthode standard d' OpenJDK renvoie l' implémentation d' interface imbriquée (soit dit en passant, c'est la seule implémentation d' interface dans cette bibliothèque de classes) et la méthode elle-même vérifie si la condition ne les prises et aucun réveil parasite ne pourraient forcer cette méthode à revenir par erreur.ReentrantLock
newCondition()
AbstractQueuedSynchronizer
Condition
ConditionObject
Condition
ConditionObject
await()
Soit dit en passant, vous pouvez le vérifier vous-même car il est assez facile d'émuler un réveil parasite une fois que l' AbstractQueuedSynchronizer
implémentation basée est impliquée.
AbstractQueuedSynchronizer
utilise à faible niveau LockSupport
de park
et unpark
méthodes, et si vous invoquez LockSupport.unpark
sur un fil en attente surCondition
, cette action ne peut être distingué d'un réveil parasite.
Refactorisant légèrement l'extrait de l'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");
}
}
, et quelle que soit la force avec laquelle le thread non principal (principal) tentera de réveiller le thread en attente, la Condition.await()
méthode ne reviendra jamais dans ce cas.
Les faux réveils sur Condition
les méthodes en attente de sont discutés dans le javadoc de l' Condition
interface . Bien qu'il le dise,
en attendant une condition, un réveil parasite peut se produire
et cela
il est recommandé que les programmeurs d'applications supposent toujours qu'ils peuvent se produire et donc toujours attendre en boucle.
mais il ajoute plus tard que
Une implémentation est gratuite pour supprimer la possibilité de réveils parasites
et AbstractQueuedSynchronizer
l'implémentation de l' Condition
interface fait exactement cela - supprime toute possibilité de réveils parasites .
Cela vaut certainement pour les autres ConditionObject
méthodes en attente.
Alors le conclusion est:
nous devons toujours appeler Condition.await
dans la boucle et vérifier si la condition ne tient pas, mais avec OpenJDK standard, la bibliothèque de classes Java ne peut jamais se produire . Sauf si, encore une fois, vous utilisez une bibliothèque de classes Java très inhabituelle (qui doit être très inhabituelle, car une autre bibliothèque de classes Java non OpenJDK bien connue, actuellement presque éteinte GNU Classpath et Apache Harmony , semble identique à l'implémentation standard de l' Condition
interface)
pthread_cond_wait()
la vraie question est "Pourquoi pthread_cond_wait a-t-il des réveils parasites?" .