6
votes

Java fil se réveiller spontanément

J'ai un thread Java fait quelque chose comme ceci: xxx

ailleurs j'ai défini la valeur comme ceci: xxx occasionnellement

occasionnellement (littéralement une fois tous les cinq millions de fois) Nextval sera défini sur NULL. J'ai jeté dans des messages de journalisation et je peux voir que l'ordre d'exécution ressemble à ceci:

  1. thread1 sets nextval à newval
  2. thread1 appels verrouillons.Notify ()
  3. thread2 se réveille de verrouiller.wait ()
  4. fil2 sets Val à NextVal
  5. thread2 se détache NextVal à NULL
  6. thread2 fait des choses avec Val
  7. Thread2 Appels Lock.Wait ()
  8. thread2 se réveille de verrouiller.wait ()
    • aucun autre thread n'a été appelé verrou.notify () et thread2 n'a pas été interrompu
    • thread2 sets Val à NextVal (NULL)
    • etc.

      J'ai explicitement vérifié et que la serrure se réveille une seconde fois, elle n'est pas interrompue.

      Est-ce que je fais quelque chose de mal ici?


5 commentaires

+1 pour une explication bien écrite, mais -1 pour ne pas lire plus attentivement le Javadoc :-)


IIRC, rien ne garantit qu'un thread ne se réveillera pas d'une attente, tandis que la condition d'attente n'est toujours pas satisfaite. Vous devez toujours vérifier la condition et ré-exécuter l'attente s'il n'est pas satisfait.


(La raison de cela a trait à la complexité de la mise en œuvre de la logique "WakeUp". Garant qu'il n'y a jamais de cassette de réveil parasite est tout à fait impossible sans beaucoup de synchronisation coûteuse.)


Wow, je fais beaucoup de filetage et c'est la première fois que je l'ai remarqué! Merci pour tous les rappels doux de relire les documents: -S


C'est l'une des choses avec le filetage, vous pouvez exécuter quelque chose d'infini moins Delta Times bien et cela pourrait toujours contenir un bug. Je recommanderais JCIP en tant que texte définitif sur les problèmes de threading . En raison des complexités inhérentes de l'approche normale de la multi-threading, vous êtes inutile, vous ne peut pas écrire et tester; Comme mentionné ci-dessus, il est presque impossible de tester entièrement le code simultané - vous avez à savoir les pièges communs à l'avance.


3 Réponses :


4
votes

yup, thread s se réveille spontanément. Le ceci est explicitement indiqué dans Le Javadoc :

"Un thread peut également se réveiller sans être notifié, interrompu ou chronométrant, un soi-disant parasidice wakeup ."

" Vous devez attendre dans une boucle. Ceci est également explicitement mentionné dans la Javadoc: xxx

dans votre cas: xxx


3 commentaires

Je dois noter que la solution «alors que la solution (NextVal == Null) {'ne fonctionnerait pas pour moi, car j'ai omis le détail que ce thread est arrêté via une méthode qui fait: synchronisé (verrouillage) {en cours d'exécution = vrai; verrouillage.notifier (); } Je ne peux donc pas tourner sur 'NextVal == null'. Au lieu de cela, j'ai ajouté: si (NextVal == null) continue; Avant le 'Val = NextVal' pour s'assurer que "Exécution" est cochée à chaque voyage à travers la boucle.


Supprimer cela entrelièrement, faites-en un tandis que (vrai) . Vérifiez le drapeau Exécuter ailleurs et retour . Vous pouvez même ajouter une certaine logique dans l'interruption, alors vous pouvez interruption le fil pour arrêter.


Oui, l'interruption est la bonne façon d'arrêter un fil d'exécution. L'utilisation d'un Boolean L'approche du drapeau est pleine de pièges. Comme, j'espère que en cours d'exécution est marqué volatile ici!



1
votes

Les wakev ups parasites sont assez courants et il est donc toujours conseillé de attendre () code> sur une condition dans une boucle.

Changez votre code comme suit: P>

 synchronized (obj) {
     while (<condition does not hold>)
         obj.wait();
     ... // Perform action appropriate to condition
 }


0 commentaires

1
votes

Cela pourrait être un réveil parasite, mais ce n'est pas la seule cause possible. C'est certainement un problème avec votre logique. Vous devez mettre l'attente à l'intérieur d'une boucle qui reteste pour la condition.

Lorsqu'un fil se réveille de l'attente, il n'a plus le verrouillage. Il a publié le verrouillage quand il a commencé à attendre, il doit réacquérir la serrure avant de pouvoir continuer. Le thread juste réveillé peut généralement être ensuite en ligne en raison de l'affinité de fil (qui est probablement la raison pour laquelle votre code fonctionne la plupart du temps), mais il y a toujours la chance que ce ne soit pas; Un autre fil peut entrer et accrocher la serrure, faire sa chose et laisser le nulval null, avant que le fil réalisé ne puisse prendre la serrure. Cela signifie que le test de null que le fil fabriqué avant l'attente n'est plus pertinent. Vous devez revenir en arrière et tester à nouveau une fois que vous avez la serrure.

Modifiez le code pour utiliser une boucle, comme: xxx

de cette façon le test est fabriqué pendant que le fil a la serrure, et tout ce qui se passe dans le bloc ci-dessous, la boucle tandis que NextVal n'est vraiment pas nulle.


0 commentaires