J’ai implémenté un profileur simple avec JVMTI pour afficher l’invocation sur wait()
et notifyAll()
. En tant que test, j’utilise le. exemple de consommateur producteur d’Oracle . J’ai les trois événements suivants:
L’invocation wait()
et son MonitorEnter
ont MonitorEnter
profilés à l’aide des événements MonitorEnter
et MonitorExit
. L’invocation notifyAll()
est profilée lorsqu’une méthode portant le nom notifyAll
est notifyAll
.
Maintenant, j’ai les résultats suivants, le premier provient du profileur lui-même et le second de Java , où j’ai placé l’instruction System.out.println
appropriée.
// Profiler: Thread-1 invoked notifyAll() Thread-0 invoked notifyAll() Thread-0 invoked notifyAll() Thread-0 invoked notifyAll() Thread-0 invoked notifyAll() Thread-0 invoked notifyAll() Thread-1 invoked notifyAll() Thread-1 invoked notifyAll() Thread-1 invoked notifyAll() Thread-1 invoked notifyAll() Thread-1 invoked notifyAll() Thread-1 invoked notifyAll() Thread-1 invoked notifyAll() Thread-1 invoked wait() Thread-1 left wait() Thread-1 invoked notifyAll() Thread-1 invoked wait() Thread-1 left wait() Thread-1 invoked notifyAll() Thread-1 invoked wait() Thread-1 left wait() Thread-1 invoked notifyAll() // Java: Thread-0 invoked notifyAll() Thread-1 invoked notifyAll() Thread-0 invoked notifyAll() Thread-1 invoked notifyAll() Thread-0 invoked notifyAll() Thread-1 invoked wait() Thread-1 invoked notifyAll() Thread-0 invoked notifyAll() Thread-1 invoked wait() Thread-1 invoked notifyAll() Thread-0 invoked notifyAll() Thread-1 invoked wait() Thread-1 invoked notifyAll()
Quelqu’un a-t-il une explication de l’origine de cet écart? notifyAll()
est appelé plusieurs fois. On m’a dit que cela pourrait être dû à des réponses faussement positives de la demande de Java au système d’exploitation.
Une demande notifyAll()
a été envoyée au système d’exploitation et une réponse faussement positive a été envoyée, ce qui donne l’impression que la demande a abouti. Puisque notifyAll
est enregistré par l’appel de la méthode de profilage au lieu de MonitorEnter
cela pourrait expliquer pourquoi cela ne se produit pas avec wait.
J’ai oublié de dire que je n’ai pas exécuté les programmes séparément, les deux journaux sont issus de la même exécution.
Informations additionnelles
Initialement ajouté comme réponse, déplacé à la question par extraneon:
Je pense avoir découvert la provenance de notifyAll en plus, j’ai ajouté le profilage du contexte de la méthode dans laquelle notifyAll est appelé:
723519: Thread-1 invoked notifyAll() in Consumer.take 3763279: Thread-0 invoked notifyAll() in Producer.put 4799016: Thread-0 invoked notifyAll() in Producer.put 6744322: Thread-0 invoked notifyAll() in Producer.put 8450221: Thread-0 invoked notifyAll() in Producer.put 10108959: Thread-0 invoked notifyAll() in Producer.put 39278140: Thread-1 invoked notifyAll() in java.util.ResourceBundle.endLoading 40725024: Thread-1 invoked notifyAll() in java.util.ResourceBundle.endLoading 42003869: Thread-1 invoked notifyAll() in java.util.ResourceBundle.endLoading 58448450: Thread-1 invoked notifyAll() in java.util.ResourceBundle.endLoading 60236308: Thread-1 invoked notifyAll() in java.util.ResourceBundle.endLoading 61601587: Thread-1 invoked notifyAll() in java.util.ResourceBundle.endLoading 70489811: Thread-1 invoked notifyAll() in Consumer.take 75068409: Thread-1 invoked wait() in Drop.take 75726202: Thread-1 left wait() in Drop.take 77035733: Thread-1 invoked notifyAll() in Consumer.take 81264978: Thread-1 invoked notifyAll() in Consumer.take 85810491: Thread-1 invoked wait() in Drop.take 86477385: Thread-1 left wait() in Drop.take 87775126: Thread-1 invoked notifyAll() in Consumer.take
Mais même sans ces appels externes, il existe de nombreux appels notifyAll qui n’apparaissent pas dans le débogage de printf.
J’ai passé un certain temps à parsingr l’exemple de producteur-consommateur fourni par Oracle et votre sortie (profileur et programme Java). Il y a des choses étranges sur vos sorties en plus des plusieurs inattendues notifyAll()
:
nous devrions nous attendre à ce que la méthode wait () s’exécute 4 fois (le tableau Ssortingng
manipulé par le producteur a 4 éléments). Le résultat de votre profileur montre qu’il n’a été exécuté que trois fois.
Une autre chose assez étrange est la numérotation des threads dans la sortie du profileur. L’exemple comporte deux threads, mais votre profileur exécute tout le code dans un seul thread, c’est Thread-1
dire Thread-1
, tandis que Thread-0
exécute notifyAll()
.
L’exemple de code fourni est correctement programmé d’un sharepoint vue concurrentiel et linguistique: wait()
et notifyAll()
des méthodes synchronisées pour garantir le contrôle du moniteur. wait condition est dans une boucle while avec les notifications correctement placées à la fin des méthodes. Cependant, j’ai remarqué que le bloc catch (InterruptedException e)
est vide, ce qui signifie que si le thread en attente est interrompu, la méthode notifyAll()
sera exécutée. Cela peut être une cause pour plusieurs plusieurs inattendues notifyAll()
.
En conclusion, sans apporter quelques modifications au code et effectuer des tests supplémentaires, il ne sera pas facile de comprendre d’où vient le problème.
En guise de remarque, je vais laisser ce lien Créer un agent de débogage et de profilage avec JVMTI pour les plus curieux qui souhaitent jouer avec JVMTI.
Si votre code contient une condition de concurrence critique, un profileur peut le ralentir suffisamment pour afficher ou masquer une erreur dans votre code. (J’aime exécuter mon programme dans un profileur, juste pour afficher les conditions de course.)
Comme notifyAll () ne notifiera que les threads wait (), l’appel de wait () après notifyAll () risque de rater la notification. c’est-à-dire qu’il est apasortingde, il ne sait pas que vous avez appelé prévenir auparavant
Si vous ralentissez votre application, notifyAll () peut être différé après le démarrage de wait ().