Si on google pour "différence entre notify()
et notifyAll()
", alors beaucoup d'explications apparaîtront (en laissant de côté les paragraphes javadoc). Tout se résume au nombre de threads en attente en cours de réveil: un dans notify()
tous notifyAll()
.
Cependant (si je comprends bien la différence entre ces méthodes), un seul thread est toujours sélectionné pour une nouvelle acquisition du moniteur; dans le premier cas celui sélectionné par la VM, dans le second cas celui sélectionné par le planificateur de threads système. Les procédures de sélection exactes pour les deux (dans le cas général) ne sont pas connues du programmeur.
Quelle est alors la différence utile entre notify () et notifyAll () ? Suis-je en train de manquer quelque chose?
java
multithreading
Sergey Mikhanov
la source
la source
synchronized
, sauf pour certains , cas d'utilisation assez rares.notifyAll()
cas, les autres threads après le premier restent éveillés et acquièrent le moniteur un par un. Dans lenotify
cas, aucun des autres threads n'est même réveillé. Donc, fonctionnellement, ils sont très différents!blocked
et nonwaiting
.Lorsqueblocked
son exécution est temporairement suspendue jusqu'à ce qu'un autre thread soit à l'intérieur dusync
bloc.Réponses:
Ce n'est pas correct.
o.notifyAll()
réveille tous les threads bloqués dans leso.wait()
appels. Les threads ne sont autorisés à revenir que l'o.wait()
un après l'autre, mais ils auront chacun leur tour.Autrement dit, cela dépend de la raison pour laquelle vos discussions attendent d'être notifiées. Voulez-vous dire à l'un des fils d'attente que quelque chose s'est produit, ou voulez-vous tous les dire en même temps?
Dans certains cas, tous les threads en attente peuvent prendre des mesures utiles une fois l'attente terminée. Un exemple serait un ensemble de threads attendant qu'une certaine tâche se termine; une fois la tâche terminée, tous les threads en attente peuvent poursuivre leurs activités. Dans un tel cas, vous utiliseriez notifyAll () pour réveiller tous les threads en attente en même temps.
Dans un autre cas, par exemple le verrouillage mutuellement exclusif, un seul des threads en attente peut faire quelque chose d'utile après avoir été notifié (dans ce cas, acquérez le verrou). Dans un tel cas, vous préférez utiliser notify () . Correctement implémenté, vous pouvez également utiliser notifyAll () dans cette situation, mais vous réveilleriez inutilement les threads qui ne peuvent rien faire de toute façon.
Dans de nombreux cas, le code d'attente d'une condition sera écrit en boucle:
De cette façon, si un
o.notifyAll()
appel réveille plusieurs threads en attente et que le premier à revenir deso.wait()
marques laisse la condition à l'état faux, les autres threads qui ont été réveillés retourneront en attente.la source
Clairement,
notify
réveille (n'importe quel) un thread de l'ensemble d'attente,notifyAll
réveille tous les threads de l'ensemble d'attente. La discussion suivante devrait dissiper tous les doutes.notifyAll
doit être utilisé la plupart du temps. Si vous ne savez pas lequel utiliser, utiliseznotifyAll
.Veuillez voir l'explication qui suit.Lisez très attentivement et comprenez. Veuillez m'envoyer un e-mail si vous avez des questions.
Regardez producteur / consommateur (l'hypothèse est une classe ProducerConsumer avec deux méthodes). IL EST CASSÉ (car il utilise
notify
) - oui cela PEUT fonctionner - même la plupart du temps, mais cela peut aussi provoquer un blocage - nous verrons pourquoi:TOUT D'ABORD,
Pourquoi avons-nous besoin d'une boucle while entourant l'attente?
Nous avons besoin d'une
while
boucle au cas où nous aurions cette situation:Le consommateur 1 (C1) entre dans le bloc synchronisé et le tampon est vide, donc C1 est mis dans le jeu d'attente (via l'
wait
appel). Le consommateur 2 (C2) est sur le point d'entrer dans la méthode synchronisée (au point Y ci-dessus), mais le producteur P1 place un objet dans le tampon, puis appellenotify
. Le seul thread en attente est C1, il est donc réveillé et tente maintenant de ré-acquérir le verrou d'objet au point X (ci-dessus).C1 et C2 tentent maintenant d'acquérir le verrou de synchronisation. L'un d'eux (non déterministe) est choisi et entre dans la méthode, l'autre est bloqué (pas en attente - mais bloqué, essayant d'acquérir le verrou sur la méthode). Disons que C2 obtient le verrou en premier. C1 bloque toujours (essaye d'acquérir le verrou en X). C2 termine la méthode et libère le verrou. Maintenant, C1 acquiert le verrou. Devinez quoi, heureusement que nous avons une
while
boucle, car C1 effectue la vérification de boucle (garde) et ne peut pas supprimer un élément inexistant du tampon (C2 l'a déjà!). Si nous n'en avions paswhile
, nous obtiendrions unIndexArrayOutOfBoundsException
comme C1 essaie de supprimer le premier élément du tampon!MAINTENANT,
Ok, maintenant pourquoi avons-nous besoin de notifier tout?
Dans l'exemple producteur / consommateur ci-dessus, il semble que nous pouvons nous en sortir
notify
. Cela semble ainsi, car nous pouvons prouver que les gardes des boucles d' attente pour le producteur et le consommateur s'excluent mutuellement. Autrement dit, il semble que nous ne pouvons pas avoir de thread en attente dans laput
méthode ainsi que dans laget
méthode, car, pour que cela soit vrai, les éléments suivants doivent être vrais:buf.size() == 0 AND buf.size() == MAX_SIZE
(supposez que MAX_SIZE n'est pas 0)CEPENDANT, ce n'est pas assez bon, nous devons utiliser
notifyAll
. Voyons pourquoi ...Supposons que nous ayons un tampon de taille 1 (pour rendre l'exemple facile à suivre). Les étapes suivantes nous mènent à l'impasse. Notez qu'à TOUT MOMENT un thread est réveillé avec notifier, il peut être sélectionné de manière non déterministe par la JVM - c'est-à-dire que n'importe quel thread en attente peut être réveillé. Notez également que lorsque plusieurs threads bloquent l'entrée d'une méthode (c'est-à-dire en essayant d'acquérir un verrou), l'ordre d'acquisition peut être non déterministe. Souvenez-vous également qu'un thread ne peut être que dans l'une des méthodes à la fois - les méthodes synchronisées permettent à un seul thread d'exécuter (c'est-à-dire de maintenir le verrou de) toutes les méthodes (synchronisées) de la classe. Si la séquence d'événements suivante se produit - des résultats de blocage:
ÉTAPE 1:
- P1 met 1 caractère dans le tampon
ÉTAPE 2:
- P2 tente
put
- vérifie la boucle d'attente - déjà un caractère - attendÉTAPE 3:
- P3 tente
put
- vérifie la boucle d'attente - déjà un caractère - attendÉTAPE 4:
- C1 tente d'obtenir 1 caractère
- C2 tente d'obtenir 1 caractère - blocs à l'entrée de la
get
méthode- C3 tente d'obtenir 1 caractère - blocs à l'entrée de la
get
méthodeÉTAPE 5:
- C1 exécute la
get
méthode - obtient la méthode char, callsnotify
, exit- Le
notify
réveille P2- MAIS, C2 entre dans la méthode avant que P2 ne puisse (P2 doit réacquérir le verrou), donc P2 bloque à l'entrée dans la
put
méthode- C2 vérifie la boucle d'attente, plus de caractères dans le tampon, donc attend
- C3 entre dans la méthode après C2, mais avant P2, vérifie la boucle d'attente, plus de caractères dans le tampon, donc attend
ÉTAPE 6:
- MAINTENANT: il y a P3, C2 et C3 en attente!
- Enfin P2 acquiert le verrou, met un caractère dans le tampon, les appels notifient, quitte la méthode
ÉTAPE 7:
- La notification de P2 réveille P3 (rappelez-vous que n'importe quel thread peut être réveillé)
- P3 vérifie la condition de la boucle d'attente, il y a déjà un caractère dans le tampon, alors attend.
- PLUS DE FILS POUR APPELER NOTIFIER ET TROIS FILS SUSPENDUS PERMANENT!
SOLUTION: Remplacer
notify
parnotifyAll
dans le code producteur / consommateur (ci-dessus).la source
notify
P3 (le thread sélectionné dans cet exemple) se poursuit à partir du point où il était en attente (c'est-à-dire dans lawhile
boucle). Il existe d'autres exemples qui ne provoquent pas de blocage, cependant, dans ce cas, l'utilisation denotify
ne garantit pas un code sans blocage. L'utilisation denotifyAll
ne.Différences utiles:
Utilisez notify () si tous vos threads en attente sont interchangeables (l'ordre dans lequel ils se réveillent n'a pas d'importance), ou si vous n'avez qu'un seul thread en attente. Un exemple courant est un pool de threads utilisé pour exécuter des travaux à partir d'une file d'attente - lorsqu'un travail est ajouté, l'un des threads est averti de se réveiller, d'exécuter le travail suivant et de se rendormir.
Utilisez notifyAll () pour d'autres cas où les threads en attente peuvent avoir des objectifs différents et devraient pouvoir s'exécuter simultanément. Un exemple est une opération de maintenance sur une ressource partagée, où plusieurs threads attendent la fin de l'opération avant d'accéder à la ressource.
la source
Je pense que cela dépend de la façon dont les ressources sont produites et consommées. Si 5 objets de travail sont disponibles à la fois et que vous avez 5 objets de consommation, il serait judicieux de réveiller tous les threads à l'aide de notifyAll () afin que chacun puisse traiter 1 objet de travail.
Si vous n'avez qu'un seul objet de travail disponible, quel est l'intérêt de réveiller tous les objets de consommation pour courir pour cet objet? Le premier vérifiant le travail disponible l'obtiendra et tous les autres threads vérifieront et découvriront qu'ils n'ont rien à faire.
J'ai trouvé une excellente explication ici . En bref:
la source
Notez qu'avec les utilitaires de simultanéité, vous avez également le choix entre
signal()
etsignalAll()
comme ces méthodes y sont appelées. La question reste donc valable même avecjava.util.concurrent
.Doug Lea soulève un point intéressant dans son célèbre livre : si un
notify()
etThread.interrupt()
se produisent en même temps, la notification pourrait en fait se perdre. Si cela peut se produire et a des implications dramatiques,notifyAll()
c'est un choix plus sûr même si vous payez le prix des frais généraux (réveiller trop de threads la plupart du temps).la source
Court résumé:
Préférez toujours notifyAll () à notify () sauf si vous avez une application massivement parallèle où un grand nombre de threads font tous la même chose.
Explication:
source: https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
Comparez notify () avec notifyAll () dans la situation décrite ci-dessus: une application massivement parallèle où les threads font la même chose. Si vous appelez notifyAll () dans ce cas, notifyAll () provoquera le réveil (c.-à-d. La planification) d'un grand nombre de threads, beaucoup d'entre eux inutilement (car un seul thread peut réellement continuer, à savoir le thread qui recevra la surveiller l'objet wait () , notify () ou notifyAll () a été appelé), gaspillant ainsi des ressources informatiques.
Ainsi, si vous n'avez pas d'application où un grand nombre de threads font la même chose simultanément, préférez notifyAll () à notify () . Pourquoi? Parce que, comme d'autres utilisateurs ont déjà répondu sur ce forum, notifiez ()
source: API Java SE8 ( https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#notify-- )
Imaginez que vous ayez une application de consommateur producteur où les consommateurs sont prêts (c'est-à-dire en attente ) à consommer, les producteurs sont prêts (c'est-à-dire en attente ) à produire et la file d'attente d'articles (à produire / consommer) est vide. Dans ce cas, notify () pourrait ne réveiller que les consommateurs et jamais les producteurs car le choix de ceux qui se réveillent est arbitraire . Le cycle de consommation des producteurs ne progressera pas, bien que les producteurs et les consommateurs soient respectivement prêts à produire et à consommer. Au lieu de cela, un consommateur est réveillé (c'est-à-dire qu'il quitte le statut wait () ), ne retire pas un élément de la file d'attente car il est vide et avertit () s un autre consommateur de continuer.
En revanche, notifyAll () réveille à la fois les producteurs et les consommateurs. Le choix qui est planifié dépend du planificateur. Bien sûr, en fonction de l'implémentation du planificateur, le planificateur peut également uniquement planifier des consommateurs (par exemple, si vous attribuez une priorité très élevée aux threads de consommateurs). Cependant, l'hypothèse ici est que le danger que le planificateur ne planifie que les consommateurs est inférieur au risque que la JVM ne réveille que les consommateurs car tout planificateur raisonnablement implémenté ne prend pas que des décisions arbitraires . Au contraire, la plupart des implémentations du planificateur font au moins un effort pour éviter la famine.
la source
Voici un exemple. Exécuter. Ensuite, changez l'un des notifyAll () pour notifier () et voir ce qui se passe.
Classe ProducerConsumerExample
Classe Dropbox
Classe consommateur
Classe de producteur
la source
De Joshua Bloch, le gourou de Java lui-même dans Effective Java 2nd edition:
"Article 69: Préférer les utilitaires de simultanéité à attendre et à notifier".
la source
J'espère que cela dissipera certains doutes.
notify () : La méthode notify () réveille un thread en attente du verrou (le premier thread qui a appelé wait () sur ce verrou).
notifyAll () : La méthode notifyAll () réveille tous les threads en attente de verrouillage; la JVM sélectionne l'un des threads dans la liste des threads en attente de verrouillage et le réveille.
Dans le cas d'un seul thread en attente d'un verrou, il n'y a pas de différence significative entre notify () et notifyAll (). Cependant, lorsque plusieurs threads attendent le verrouillage, dans notify () et notifyAll (), le thread exact réveillé est sous le contrôle de la JVM et vous ne pouvez pas contrôler par programmation le réveil d'un thread spécifique.
À première vue, il semble que ce soit une bonne idée d'appeler simplement notify () pour réveiller un thread; il peut sembler inutile de réveiller tous les fils. Cependant, le problème avec notify () est que le thread réveillé peut ne pas être celui qui convient pour être réveillé (le thread peut attendre une autre condition, ou la condition n'est toujours pas satisfaite pour ce thread, etc.). Dans ce cas , la notification () peut être perdue et aucun autre thread ne se réveillera, ce qui pourrait entraîner un type de blocage (la notification est perdue et tous les autres threads attendent une notification - pour toujours).
Pour éviter ce problème , il est toujours préférable d'appeler notifyAll () lorsqu'il y a plus d'un thread en attente d'un verrou (ou plus d'une condition sur laquelle l'attente est effectuée). La méthode notifyAll () réveille tous les threads, elle n'est donc pas très efficace. cependant, cette perte de performances est négligeable dans les applications du monde réel.
la source
Il existe trois états pour un thread.
Désormais, lorsqu'une notification () est appelée, la JVM sélectionne un thread et les déplace vers l'état BLOQUÉ et donc vers l'état EN COURS car il n'y a pas de concurrence pour l'objet moniteur.
Lorsqu'un notifyAll () est appelé, la JVM sélectionne tous les threads et les déplace tous vers l'état BLOQUÉ. Tous ces threads obtiendront le verrou de l'objet sur une base prioritaire. Le thread qui est capable d'acquérir le moniteur en premier sera capable de passer d'abord à l'état RUNNING et ainsi de suite.
la source
Je suis très surpris que personne n'ait mentionné le fameux problème de "réveil perdu" (google it).
Fondamentalement:
ALORS, vous devez utiliser notifyAll sauf si vous avez des garanties prouvables que les réveils perdus sont impossibles.
Un exemple courant est une file d'attente FIFO simultanée où: plusieurs mises en file d'attente (1. et 3. ci-dessus) peuvent faire passer votre file d'attente de vides à non-vides plusieurs files d'attente (2. ci-dessus) peuvent attendre la condition "la file d'attente n'est pas vide" vide -> non vide doit informer les dequeuers
Vous pouvez facilement écrire un entrelacement d'opérations dans lequel, à partir d'une file d'attente vide, 2 enqueuers et 2 dequeuers interagissent et 1 enqueuer reste en veille.
Il s'agit d'un problème sans doute comparable au problème de blocage.
la source
notify()
réveillera un thread tandis quenotifyAll()
réveillera tout. Pour autant que je sache, il n'y a pas de compromis. Mais si vous n'êtes pas sûr de ce quenotify()
feront vos threads, utiliseznotifyAll()
. Fonctionne comme un charme à chaque fois.la source
Toutes les réponses ci-dessus sont correctes, pour autant que je sache, donc je vais vous dire autre chose. Pour le code de production, vous devez vraiment utiliser les classes dans java.util.concurrent. Ils ne peuvent pas faire grand-chose pour vous, dans le domaine de la concurrence en Java.
la source
notify()
vous permet d'écrire du code plus efficace quenotifyAll()
.Considérez le morceau de code suivant qui est exécuté à partir de plusieurs threads parallèles:
Il peut être rendu plus efficace en utilisant
notify()
:Dans le cas où vous avez un grand nombre de threads, ou si la condition de la boucle d'attente est coûteuse à évaluer,
notify()
sera beaucoup plus rapide quenotifyAll()
. Par exemple, si vous avez 1000 threads, 999 threads seront réveillés et évalués après le premiernotifyAll()
, puis 998, puis 997, etc. Au contraire, avec lanotify()
solution, un seul fil sera réveillé.À utiliser
notifyAll()
lorsque vous devez choisir le thread qui fera le travail suivant:Enfin, il est important de comprendre qu'en cas de
notifyAll()
, le code à l'intérieur dessynchronized
blocs qui ont été réveillés sera exécuté séquentiellement, pas tous en même temps. Disons qu'il y a trois threads en attente dans l'exemple ci-dessus, et le quatrième thread appellenotifyAll()
. Les trois threads seront réveillés mais un seul commencera l'exécution et vérifiera l'état de lawhile
boucle. Si la condition esttrue
, il appellera àwait()
nouveau, et alors seulement le deuxième thread commencera à s'exécuter et vérifiera sawhile
condition de boucle, et ainsi de suite.la source
Voici une explication plus simple:
Vous avez raison de dire que si vous utilisez notify () ou notifyAll (), le résultat immédiat est qu'exactement un autre thread va acquérir le moniteur et commencer à s'exécuter. (En supposant que certains threads ont en fait été bloqués lors de l'attente de cet objet, d'autres threads non liés n'absorbent pas tous les cœurs disponibles, etc.) L'impact survient plus tard.
Supposons que les threads A, B et C attendent sur cet objet et que le thread A récupère le moniteur. La différence réside dans ce qui se passe une fois que A libère le moniteur. Si vous avez utilisé notify (), alors B et C sont toujours bloqués dans wait (): ils n'attendent pas sur le moniteur, ils attendent d'être notifiés. Lorsque A relâche le moniteur, B et C seront toujours assis là, attendant une notification ().
Si vous avez utilisé notifyAll (), alors B et C ont tous deux avancé au-delà de l'état "attendre la notification" et attendent tous deux d'acquérir le moniteur. Lorsque A libère le moniteur, B ou C l'acquièrent (en supposant qu'aucun autre thread n'est en concurrence pour ce moniteur) et commence à s'exécuter.
la source
Cette réponse est une réécriture graphique et une simplification de l'excellente réponse de xagyg , y compris des commentaires par eran .
Pourquoi utiliser notifyAll, même lorsque chaque produit est destiné à un seul consommateur?
Considérez les producteurs et les consommateurs, simplifiés comme suit.
Producteur:
Consommateur:
Supposons que 2 producteurs et 2 consommateurs partagent un tampon de taille 1. L'image suivante illustre un scénario conduisant à un blocage , qui serait évité si tous les threads utilisés notifientAll .
Chaque notification est étiquetée avec le fil en cours de réveil.
la source
Je voudrais mentionner ce qui est expliqué dans Java Concurrency en pratique:
Premier point, que ce soit Notify ou NotifyAll?
Ce problème peut être résolu en utilisant l'objet Condition de verrouillage à verrouillage explicite, fourni dans jdk 5, car il fournit une attente différente pour chaque prédicat de condition. Ici, il se comportera correctement et il n'y aura pas de problème de performance car il appellera le signal et s'assurera qu'un seul thread attend cette condition
la source
notify()
- Sélectionne un thread aléatoire dans l'ensemble d'attente de l'objet et le met dans l'BLOCKED
état. Les autres threads de l'ensemble d'attente de l'objet sont toujours dans leWAITING
état.notifyAll()
- Déplace tous les threads de l'ensemble d'attente de l'objet à l'BLOCKED
état. Une fois que vous l'utiliseznotifyAll()
, il n'y a plus de threads dans l'ensemble d'attente de l'objet partagé car tous sont maintenant enBLOCKED
état et non dansWAITING
état.BLOCKED
- bloqué pour l'acquisition du verrou.WAITING
- en attente de notification (ou bloqué pour la fin de la jointure).la source
Tiré du blog sur Java efficace:
Donc, ce que je comprends (du blog susmentionné, commentaire de "Yann TM" sur la réponse acceptée et les documents Java ):
la source
Jetez un œil au code publié par @xagyg.
Supposons que deux threads différents attendent deux conditions différentes:
le premier thread attend
buf.size() != MAX_SIZE
et le second thread attendbuf.size() != 0
.Supposons à un moment donné qu'il
buf.size()
n'est pas égal à 0 . La JVM appelle à lanotify()
place denotifyAll()
et le premier thread est notifié (pas le second).Le premier thread est réveillé, vérifie
buf.size()
ce qui pourrait revenirMAX_SIZE
et revient à l'attente. Le deuxième thread n'est pas réveillé, continue d'attendre et n'appelle pasget()
.la source
notify()
réveille le premier thread qui a appeléwait()
le même objet.notifyAll()
réveille tous les threads qui ont appeléwait()
le même objet.Le thread de priorité la plus élevée s'exécutera en premier.
la source
notify()
ce n'est pas exactement " le premier fil ".notifier notifiera seulement un thread en attente, tandis que notifier tous notifiera tous les threads en attente maintenant tous les threads notifiés et tous les threads bloqués sont éligibles pour le verrou, dont un seul obtiendra le verrou et tous les autres (y compris ceux qui sont en attente plus tôt) seront bloqués.
la source
Pour résumer les excellentes explications détaillées ci-dessus, et de la manière la plus simple à laquelle je puisse penser, cela est dû aux limites du moniteur intégré JVM, qui 1) est acquis sur l'ensemble de l'unité de synchronisation (bloc ou objet) et 2) ne fait aucune discrimination quant à la condition spécifique attendue / notifiée le / à propos.
Cela signifie que si plusieurs threads attendent dans des conditions différentes et que notify () est utilisé, le thread sélectionné peut ne pas être celui qui ferait progresser la condition nouvellement remplie - provoquant ce thread (et d'autres threads actuellement en attente qui pourraient pour remplir la condition, etc.) pour ne pas pouvoir progresser, et éventuellement mourir de faim ou raccrocher un programme.
En revanche, notifyAll () permet à tous les threads en attente d'acquérir à nouveau le verrou et de vérifier leur état respectif, permettant ainsi éventuellement de progresser.
Donc notifier () ne peut être utilisé en toute sécurité que si un thread en attente est garanti pour permettre la progression s'il est sélectionné, ce qui est généralement satisfait lorsque tous les threads dans le même moniteur vérifient une seule et même condition - une situation assez rare cas dans des applications du monde réel.
la source
Lorsque vous appelez l'attente () de l '"objet" (en attendant que le verrou d'objet soit acquis), interne cela libérera le verrou sur cet objet et aidera les autres threads à verrouiller cet "objet", dans ce scénario il y aura plus d'un thread en attente de la "ressource / objet" (étant donné que les autres threads ont également émis l'attente sur le même objet ci-dessus et en cours de route, il y aura un thread qui remplira la ressource / l'objet et invoquera notify / notifyAll).
Ici, lorsque vous émettez la notification du même objet (du même côté / de l'autre côté du processus / code), cela libérera un thread unique bloqué et en attente (pas tous les threads en attente - ce thread libéré sera choisi par JVM Thread Planificateur et tout le processus d'obtention de verrouillage sur l'objet est le même que normal).
Si vous n'avez qu'un seul thread qui partagera / travaillera sur cet objet, il est correct d'utiliser la méthode notify () seule dans votre implémentation wait-notify.
si vous êtes dans une situation où plus d'un thread lit et écrit sur des ressources / objets en fonction de votre logique métier, alors vous devriez opter pour notifyAll ()
maintenant je regarde comment exactement le jvm identifie et brise le thread en attente lorsque nous émettons notify () sur un objet ...
la source
Bien qu'il y ait quelques réponses solides ci-dessus, je suis surpris par le nombre de confusions et de malentendus que j'ai lus. Cela prouve probablement l'idée que l'on devrait utiliser java.util.concurrent autant que possible au lieu d'essayer d'écrire son propre code concurrent cassé. Retour à la question: pour résumer, la meilleure pratique aujourd'hui consiste à ÉVITER de notifier () dans TOUTES les situations en raison du problème de réveil perdu. Quiconque ne comprend pas cela ne devrait pas être autorisé à écrire du code d'accès concurrentiel critique. Si vous êtes préoccupé par le problème de regroupement, un moyen sûr de réveiller un thread à la fois est de: 1. Construire une file d'attente explicite pour les threads en attente; 2. Demandez à chaque thread de la file d'attente d'attendre son prédécesseur; 3. Demandez à chaque appel de thread notifierAll () une fois terminé. Ou vous pouvez utiliser Java.util.concurrent. *,
la source
Runnable
implémentation) traite le contenu d'une file d'attente. Lewait()
est ensuite utilisé chaque fois que la file d'attente est vide. Et lenotify()
est appelé lorsque des informations sont ajoutées. -> dans un tel cas, il n'y a qu'un seul thread qui appelle lewait()
, alors cela ne semble pas un peu idiot d'utiliser unnotifyAll()
si vous savez qu'il n'y a qu'un seul thread en attente.Réveiller tout n'a pas beaucoup d'importance ici. attendre notifier et notifier tous, tout cela est mis après avoir possédé le moniteur de l'objet. Si un thread est en attente et qu'un avis est appelé, ce thread prendra le verrou et aucun autre thread à ce stade ne pourra prendre ce verrou. L'accès simultané ne peut donc pas avoir lieu du tout. Pour autant que je sache, tout appel à attendre notifier et notifier ne peut être effectué qu'après avoir pris le verrou sur l'objet. Corrigez-moi si je me trompe.
la source