J'ai trouvé sur ce site quelques questions intéressantes (par exemple, Celui-ci ) sur le Effets de visibilité des variables volatiles dans Java est originaire de ce paragraphe tiré du livre Java Concurrence dans la pratique : p>
Les effets de visibilité des variables volatiles s'étendent au-delà de la valeur de la variable volatilienne elle-même. Lorsque le thread A écrit à une variable volatile et ensuite, le thread B lit cette même variable, les valeurs de toutes les variables visibles à A avant la rédaction de la variable volatilienne deviennent visibles à B après la lecture de la variable volatil. Donc, à partir d'une perspective de la visibilité de la mémoire, la rédaction d'une variable volatile est comme quitter un bloc synchronisé et la lecture d'une variable volatile est comme la saisie d'un bloc synchronisé. P> blockQuote>
Il y a cependant un scénario qui n'est pas complètement clair pour moi même après avoir lu les réponses aux questions connexes sur ce site, concrètement: p>
Quels seraient les effets d'un fil A0 em> écrire à la même variable volatile
avant forte> le fil a em>? En d'autres termes: A0 em> écrit la variable volatil, cette valeur est ultérieure écrasée par a em> (qui ne lit pas la variable forte>) et ensuite lue par b em>. (Nous avons donc deux opérations d'écriture de différents threads ( A0 em> et a em>) et une opération de lecture à partir d'un troisième thread ( B em>)). / p> Puis-je supposer en toute sécurité que a em> et b em> sont garantis pour voir tout fort> visible pour A0 em> Avant a0 em> écrit à la variable volatile? p>
mise à jour: strong> p> Ceci est une question conceptuelle sur le modèle de mémoire Java. Je sais que je ne peux pas prédire l'ordre dans lequel écrit à la variable volatile se produit dans les threads a0 em> et a em> et le fil de lecture b em>. Cependant, juste pour faciliter la discussion, disons que a0 em> commence beaucoup de temps avant a em> le fait, et après une autre quantité importante b em> démarre, et permet de simplifier l'hypothèse que ceci est Assez pour garantir que les écritures et les écrits se produisent dans l'ordre décrit (je sais que la commande ne peut être garantie par chronométrage uniquement, il s'agit simplement d'une simplification afin d'éviter de diverger de la question initiale). P>
4 Réponses :
Puis-je supposer en toute sécurité que les deux A et B sont garantis pour voir tout ce qui était visible vers A0 avant que A0 a écrit à la variable volatile? P> blockQuote>
L'écriture à un volatilité ne donne pas de fil ne se produit pas - avant des garanties sur les lectures. P>
Lorsque
B code> lit, il ne verra rien A0 pouvait voir à condition qu'il voit la mise à jour qu'elle fait.
B CODE> peut également voir quelque chose
A code> pourrait voir à condition qu'il voit la valeur
a code> a écrit. Sinon
B code> pourrait voir l'état plus ancien que l'un ou l'autre des fils si la valeur qu'il se lit n'est pas provenant de ce fil, c'est-à-dire trop tôt. P>
Si
A0 code> tente d'écrire en premier, il peut compléter après
A code> par exemple. Dites
A code> est sur la même prise que la copie propre des données. Il peut y accéder avant
A0 code> car le plus tard prendra plus de temps pour acquérir la ligne de cache. C'est celui qui termine le dernier qui détermine l'état éventuel des données. P>
Je ne suis pas sûr de bien comprendre votre réponse. Je sais que le fil b pourrait voir tout ce que A0 pourrait voir à condition qu'il voit la mise à jour A0 faite à la variable. Mais ma question est la suivante: Que se passe-t-il si la valeur écrite par A0 a été écrasée par A avant B lit la valeur (SO B verra réellement la valeur écrite par A, pas A0). B peut toujours voir tout ce que A0 pourrait voir? Et dans ce cas, peut-on voir tout ce que A0 pourrait voir? J'ai essayé de reformuler un peu ma question pour le rendre plus clair ce qui est le scénario exact dont j'ai des doutes.
Si B voit une valeur après une mise à jour A0 Fabriquée, B verra quelque chose au moins aussi courant que A0 pouvait voir. Le fait qu'une mise à jour, la même information n'a pas d'importance. A ne voit pas ce que A0 pourrait voir car il ne lit pas la variable volatile.
Que voulez-vous dire avec 'il voit la mise à jour que cela fait'? Dans l'exemple de code dans ma réponse, B ne "voir" pas la mise à jour de A0 (la valeur écrite par A0), mais elle synchronise avec elle.
@Ishtar Vous avez raison que B verra des valeurs A pourrait lire et toutes les valeurs non volatiles qu'il a écrites avant d'écrire sur la variable volatil.
@Ishtar votre fil b seulement se synchronise avec le fil et la valeur de graine du fil A0, car votre filetage A synchronise avec filetage A0
Peter, si je comprenais correctement, vous dites que sous mon scénario thread b est garanti de voir à la fois ce que A0 et A pouvaient voir. Cela semble contredire la réponse de @veritas. Pourriez-vous clarifier?
@ Sergio "Garanties ... pourrait" n'a pas de sens. B et seulement B est garanti de voir ce que A0 et A pourrait lire si elle se lit après avoir écrit. Il n'y a pas de garantie entre A et A0, pas même le commandement de l'écriture.
@Sergio Il y a un gros problème avec votre question. Vous dites dans la section mise à jour "< Je sais que la commande ne peut être garantie par chronométrage uniquement i>", mais poser des questions sur un scénario qui n'a aucun sens si nous supposons que le timing était une chose. Si un thread lit une variable code> Volatile code> écrite par deux threads, le thread de lecture n'a aucun moyen de détecter que les deux écrivies sont arrivées. La spécification indique clairement qu'il y a une relation avant i> relation entre l'écriture et le thread lisez ensuite cette valeur i>, pas pour l'écriture dont la valeur n'a jamais été lue.
Je pense que le scénario est très simple.
A0 - Écrivez à la variable volatile A - écrire à la variable volatile maintenant p> Puis-je supposer en toute sécurité que A et B sont garantis pour tout voir
c'était visible pour A0 avant que A0 a écrit à la variable volatile? P>
blockQuote> Si le thread A est seulement écrit à La même chose va avec le fil b Il dépend de quelle valeur B voit après la lecture de la valeur de V. p> si B lit la valeur à V pour que ce soit alors il verra tout Ce qui s'est passé avant l'écriture de V dans le fil A. p> si B lit la valeur à V pour être wa0, il verra tout ce qui s'est passé avant l'écriture de V dans le fil A0. P> Veuillez également garder dans l'esprit que p> Vous devez comprendre le code dans le fil b est incorrectement synchronisé
volatile donne des garanties liées à la visibilité et à la réorganisation, mais pas d'atomicité. p> Donc, même si le thread B lit la valeur de v pour être "wa" et sa garantie de voir tout ce qui s'est passé dans le fil a Cela ne signifie pas nécessairement que cela verra la valeur d'A comme 3, car il pourrait très bien que cela puisse arriver que, après la lecture de la valeur de V en tant que WA Thread A0 écrit sur A comme 1 fort> qui devient disponible enfiler B et ainsi faire échouer votre assertion. Se passe-t-il avant de vous garantir que tout ce qui doit arriver avant qu'une écriture à V soit déjà arrivé, mais cela ne signifie pas que vous ne pouvez pas voir les valeurs futures.
Vous pouvez facilement reproduire le scénario en faisant quelque chose comme ça p>
while(true) {
if(v == 2) {
assert (c == 2); // will pass
assert (b == 1); // will pass
assert (a == 2); // will pass
break;
}
if(v == 1) {
assert (b == 1); // will pass
assert (c == 2); // may fail if T2 hasn't run till
assert (a == 1); // may fail if T2 has now run setting a == 2
break;
}
}
La valeur de la valeur est hors de propos? Je ne trouve aucune référence pour pourquoi la valeur compte ..
Vous voulez donc dire que B, après avoir lu la variable volatil, pourrait voir tout ce qui était visible à un mais il n'y a aucune garantie que b pourrait voir tout ce qui était visible à A0? Cela semble contradictoire à la réponse de @lshtar, où il dit que B devrait voir tout ce qui était visible pour A0 et A (dans le scénario où un écrase la valeur initialement écrite par A0). Mais je ne suis pas sûr si j'ai mal compris quelque chose.
@Ishtar See doc.oracle .Com / Javase / Spécifications / JLS / SE7 / HTML / ... . Nous avons besoin de deux actions pour établir ce qui se passe auparavant. La deuxième action est la lecture de cette valeur dans ce cas
@Sergio vous supposez A0 exécuté avant le thread A. Supposez-vous d'abord tria a exécuté puis sur le thread b exécuté et lorsque le thread B trouve la condition V == «wa» immédiatement après que le fil A0 est exécuté, vous pouvez établir la course.
Oui, une hypothèse fondamentale de ma question est que A0 écrit la valeur volatile avant la rémunération. Le code indiqué par @lshtar illustre le scénario exact. Sous cette hypothèse est correcte la réponse de @lshtar?
@Sergio Vous ne pouvez jamais déterminer quel thread a défini la valeur en premier. Vous ne pouvez donc pas faire cette hypothèse dans votre code. Sauf si bien sûr, vous n'arrêtez pas la progression du filetage A à moins que le fil A0 a défini la valeur, similaire à ce que ISTAR a fait en utilisant tandis que la boucle dans le fil A.
Je sais que je ne peux pas déterminer la commande. J'essaie simplement de comprendre les aspects subtils du modèle de mémoire Java et de toutes les règles qui régissent des variables volatiles. Notez que je n'ai pas de code en mettant en œuvre le scénario, ma question était originaire après avoir lu le paragraphe du livre que j'ai mentionné.
Java Concurrence dans la pratique ne donne pas beaucoup d'idées. C'est un excellent livre mais c'est juste la pointe de l'iceberg. Veuillez lire ce blog shipilev.net/blog/2014/jmm-pragmatiques . Cela effacera probablement vos doutes. En savoir plus sur la cohérence séquentielle, vous pouvez facilement relier le modèle de mémoire Java
Merci. Je conclus de vos réponses que sous l'hypothèse selon laquelle A0 écrit la variable volatile avant le fait, la réponse de @LSHTAR est correcte.
@veritas. Je veux dire '' son seul si le fil a lit la variable V et la valeur qu'il se lit s'avère être WA0, alors que le thread thread A est garanti de voir tout ce qui est visible à A0 avant qu'il ne soit écrit à V '' devrait être "ce que Le fil a lit la variable V que sur le thread A est garanti de tout voir visible à A0 avant d'écrire à V ''. Et similaires pour B. Il existe une HB, quelle que soit la valeur lue, si la lecture est plus tard dans l'ordre de synchronisation. Bien entendu lire la valeur écrite, implique que l'ordre de synchronisation. Mais ce n'est pas nécessaire, dans cette question conceptuelle, à mesure que la commande est donnée.
@Ishtar no ishtar C'est une déclaration erronée, supposons que si vous lisez V dans le fil a et que la valeur s'avère être du XYZ, alors comment pouvez-vous conclure que cela verra tout voir visible à A0 avant d'écrire à V, ce n'est pas Même les Gaurantes que si A0 a écrit quelque chose de V. Vous pouvez facilement imaginer le scénario s'il y a un autre fil (x) écrit sur V, alors comment détermineriez-vous ce qui est visible à un? tout visible pour A0 ou tout visible pour X.
"Comme la commande est donnée." Je pense que c'est le problème principal - en fait, vous supposez quelque chose qui lui-même n'a pas de racine correcte. Par conséquent, tout raisonnement sera tombé à plat. Si vous entrez dans les détails de la mise en œuvre, ce sera peut-être ce que vous dites est vrai. Mais la mise en œuvre de JMM peut changer très facilement demain tout optimisation de compilateur ou processeur avec une commande de mémoire faible peut briser cela. Nous devons nous tenir aux règles spécifiées par la langue
@veritas. Sergio ne demande pas "le problème principal". Dans la question mise à jour, l'ordre de synchronisation est donné. (Sergio reconnaît le fait qu'il soit conceptuel.) Les JLS (les règles de la langue) indiquent clairement que, dans cette commande de synchronisation, il y a un HB de la lecture aux deux écrit, peu importe i> de la valeur lire.
@Ishtar oui c'est ce que j'ai répondu. Voir ma réponse pour le fil T3 où j'ai incorporé cette hypothèse.
@Ishtar: Si vous supposez que "l'ordre de synchronisation est donné", il s'agit d'un non-sens complet pour discuter de ce que le volatile code> lit et écrire faire car la relation HB est déjà là, tout comme une prémisse de la question comme "l'ordre de synchronisation est donné". Vous pouvez discuter d'un scénario hypothétique où vous appliquez une commande de synchronisation sans établir une relation HB pour ajouter que HB à l'aide de variables volatiles par la suite, mais depuis que le JMM n'offre pas un tel scénario, c'est inutile.
Une écriture à une variable volatile V (§8.3.1.4) Synchronise - avec toutes les lectures ultérieures de V par n'importe quel thread (où "suivant" est défini en fonction de l'ordre de synchronisation). JLS 17.4.4 A > p>
Ainsi, l'écriture de A0 se synchronise avec em> la lecture de B. Tous les écrit plus tôt de A0 se produisent avant em> les lectures suivantes de B. S'il n'y a pas d'autre écrit à Ces variables, alors oui, b incontens -nent les voir. P>
Je ne trouve pas qu'il se synchronise avec A0. Ainsi, aucune garantie sur la visibilité de A. P>
de la question mise à jour, nous avons l'exécution suivante, où A0, A1 et B sont des threads, T0, T1, T2 sont des déclarations et de la rétablissement écouteur. / lit à volatil v: p>
xxx pré> Les flèches désignent HappenSbefore em>. Nous avons t0 et t1 arrive avant em> t2. T2 verra les mises à jour de T0 et T1. P>
Ainsi, oui: B est garanti de tout voir écrit par A0 dans T0. Et il lira la valeur volatile écrite par a. P>
mais pas strong> t0 arrive avant em> t, ni vice versa. Ainsi, non: A n'est pas garanti de tout voir écrit par A0. P>
Cependant, ce n'est que conceptuel, dans la vie réelle B ne pouvait pas savoir si A0 est exécuté du tout. P> blockquote>
Donc, vous voulez dire que B, après avoir lu la variable volatil, vous pouvez voir tout ce qui était visible à la fois A0 et A avant leur écriture à la variable volatil, mais rien ne garantit que A pourrait voir tout ce qui était visible pour A0? La première partie semble contradictoire à la réponse de @veritas, où il dit que B verra tout ce qui était visible à un, mais pas A0 (dans le scénario où un écrase la valeur écrite à l'origine par A0). Mais je ne suis pas sûr si j'ai mal compris quelque chose.
@Sergio. Si vous pouviez poster du code, je vous dirai quelles exécutions sont autorisées / possibles. B voit les écrit d'A et A0 (ou plus tard écrit). A et A0 pourraient et ne peuvent pas se voir écrit. Sauf si elles se synchronisent d'une manière ou d'une autre, CODE PLZ;)
@Ishtar Votre cette déclaration est fausse "B n'a pas à" voir la mise à jour "pour voir les autres écrivies". Dans votre exemple, vous avez déjà établi que v = wa implique v = wa0 c'est pourquoi vous affirmez en B ne ferez pas échouer. c'est déjà une chaîne arrive auparavant. Si vous supprimez tout en boucle dans le fil a, votre affirmation dans le fil B échouera.
@ishtar c'est une règle simple si un HB B et B HB c puis cela implique une HB C.
Lshtar merci pour votre réponse. Je comprends ce que vous dites, mais ma seule préoccupation est que cela semble contredire la réponse de @veritas et je ne sais plus qui a raison :( (Je suis reconnaissant pour votre aide indépendamment de qui a raison).
@LSHTAR, vient de réaliser que l'exemple que vous avez fourni est incorrect car le filetage A est à la fois en train de lire et d'écrire la variable volatil. La question était de savoir ce qui se passe si un écrit juste sur la variable (cela ne le lit pas). Quels changements dans votre réponse sous ce correctif? Précisez s'il vous plaît.
@Sergio. En effet, merci! J'ai enlevé l'exemple. Clarifié et adopté à la question mise à jour.
Puis-je supposer en toute sécurité que les deux A et B sont garantis pour voir tout ce qui était visible vers A0 avant que A0 a écrit à la variable volatile? P>
non fort>. Avec des magasins volatils, le provenance avant le bord est construit lorsqu'il y a un prédicat impliqué. Par exemple, dans l'ordre du thread A code> pour voir tous les écrivies effectuées par
A0 code> s écrit il a besoin d'affirmer sur l'écriture volatile. P>
int a,b; volatile c; A0: a = 10; b = 5; c = 20; A: int l = c; // c could be either 20 or 0 int j = a; // a could be either 10 or 0 int k = b; // b could be either 5 or 0 c = 50; B: if(c == 50){ int l = c; // c can be 20 or 50. c cannot be 0 int j = a; // a could be either 10 or 0 int k = b; // b could be either 5 or 0 }
Merci pour votre réponse John. Pourriez-vous mettre à jour votre exemple en prise en compte que le thread A ne lit pas la variable volatilse? Je viens de mettre à jour ma question pour souligner que c'est le scénario que je demande.
@Johnvint L = C n'est pas garanti d'être 50 dans le fil b, il y a une course de données. A0 (C = 20) peut simplement exécuter après que la condition IF est évaluée à true dans le fil B. Vous avez besoin de plus que ce qui se passe avant que Gaureree L = C soit 50
Variable volatil garantie simplement que le lecteur lira la dernière valeur mais ne garantit pas l'évitement de la condition de race.
Oui. C'est exactement la même règle, avec B remplacé par A0.
Qu'entendez-vous par «avec B remplacé par A0» @jbnizet? Est-ce que mon hypothèse est correcte ou non? Merci pour la clarification!
Question interessante. Je suis coincé sur ce que signifie "tout" signifie exactement? "Tout" signifie-t-il que toutes les autres variables pouvant exister sur l'objet qui possède le Var volatil? Cela signifie-t-il un objet non volatil que Thread A0 a accès à et est mis en cache dans le contexte de Fil A A 0 mais non mis en cache dans le contexte de thread B?
@Josemartinez tout signifie variables partagées entre les trois threads pouvant être volatils et non volatile.