Quand exactement utilisez-vous le mot-clé volatile en Java?

J’ai lu ” Quand utiliser” volatile “en Java? ” Mais je suis toujours confus. Comment savoir quand je devrais marquer une variable volatile? Que se passe-t-il si je me trompe, soit en omettant un volatil sur quelque chose qui en a besoin ou en mettant de la volatilité sur quelque chose qui ne l’est pas? Quelles sont les règles générales pour déterminer quelles variables doivent être volatiles dans le code multithread?

En gros, vous l’utilisez lorsque vous voulez que la variable membre soit accessible par plusieurs threads, mais que vous n’avez pas besoin d’une atomicité composée (vous ne savez pas si c’est la bonne terminologie).

class BadExample { private volatile int counter; public void hit(){ /* This operation is in fact two operations: * 1) int tmp = this.counter; * 2) this.counter = tmp + 1; * and is thus broken (counter becomes fewer * than the accurate amount). */ counter++; } } 

Ce qui précède est un mauvais exemple, car vous avez besoin d’une atomicité composée.

  class BadExampleFixed { private int counter; public synchronized void hit(){ /* * Only one thread performs action (1), (2) at a time * "atomically", in the sense that other threads can not * observe the intermediate state between (1) and (2). * Therefore, the counter will be accurate. */ counter++; } } 

Maintenant à un exemple valide:

  class GoodExample { private static volatile int temperature; //Called by some other thread than main public static void todaysTemperature(int temp){ // This operation is a single operation, so you // do not need compound atomicity temperature = temp; } public static void main(Ssortingng[] args) throws Exception{ while(true){ Thread.sleep(2000); System.out.println("Today's temperature is "+temperature); } } } 

Maintenant, pourquoi ne pouvez-vous pas simplement utiliser private static int temperature ? En fait, vous pouvez (dans le sens où votre programme ne va pas exploser ou quelque chose), mais le changement de temperature par l’autre thread peut ou peut ne pas être “visible” sur le thread principal.

Fondamentalement, cela signifie qu’il est même possible que votre application. continue à écrire Today's temperature is 0 pour toujours si vous n’utilisez pas volatile (dans la pratique, la valeur a tendance à devenir visible). Cependant, vous ne devriez pas risquer d’utiliser des volatiles lorsque cela est nécessaire. objects complètement construits, etc.).

Si vous mettez un mot clé volatile sur quelque chose qui n’a pas besoin de volatile , cela n’affectera pas la correction de votre code (c.-à-d. Que le comportement ne changera pas). En termes de performances, cela dépendra de l’implémentation de la JVM. En théorie, vous pourriez avoir une petite dégradation des performances car le compilateur ne peut pas réorganiser les optimisations, invalider le cache du processeur, etc., mais le compilateur peut prouver que votre champ ne peut jamais être accédé par plusieurs threads mot-clé complètement et le comstackr à des instructions identiques.

MODIFIER:
Réponse à ce commentaire:

Ok, mais pourquoi ne pouvons-nous pas synchroniser todaysTemperature et créer un getter synchronisé pour la température?

Vous pouvez et il se comportera correctement. Tout ce que vous pouvez avec volatile peut être fait avec synchronized , mais pas l’inverse. Il y a deux raisons pour lesquelles vous pourriez préférer le volatile si vous le pouvez:

  1. Moins enclin aux bogues: Cela dépend du contexte, mais dans de nombreux cas, l’utilisation de
  2. Plus performant: dans la plupart des mises en œuvre de JVM, la volatile peut avoir un débit considérablement plus élevé et une meilleure latence. Cependant, dans la plupart des applications, la différence est trop petite pour être importante.

La volatilité est la plus utile dans les algorithmes sans locking. Vous marquez la variable contenant des données partagées comme étant volatile lorsque vous n’utilisez pas le locking pour accéder à cette variable et vous souhaitez que les modifications apscopes par un thread soient visibles dans un autre ou que vous souhaitiez créer une relation pas de nouveau commandé pour que les changements soient visibles au moment opportun.

Le Cookbook JMM décrit les opérations qui peuvent être réorganisées et celles qui ne le peuvent pas.

Le volatile peut également être utilisé pour publier en toute sécurité des objects immuables dans un environnement multithread.

Déclarer un champ comme public volatile ImmutableObject foo garantit que tous les threads voient toujours la référence d’instance actuellement disponible.

Voir la concurrence dans la pratique Java pour plus d’informations sur ce sujet.

volatile keyword garantit que la valeur de la variable volatile sera toujours lue depuis la mémoire principale et non depuis le cache local de Thread.

Du tutoriel java sur la concurrence:

L’utilisation de variables volatiles réduit le risque d’erreurs de cohérence de la mémoire, car toute écriture sur une variable volatile établit une relation “passe avant” avec les lectures ultérieures de cette même variable

Cela signifie que les modifications apscopes à une variable volatile sont toujours visibles par les autres threads. Cela signifie également que lorsqu’un thread lit une variable volatile, il ne voit pas seulement les dernières modifications apscopes au volatil, mais également les effets secondaires du code qui a conduit à la modification.

En ce qui concerne votre requête:

Comment savoir quand je devrais marquer une variable volatile? Quelles sont les règles générales pour déterminer quelles variables doivent être volatiles dans le code multithread?

Si vous estimez que tous les threads de lecture obtiennent toujours la dernière valeur d’une variable, vous devez marquer la variable comme volatile

Si vous avez un thread d’écriture pour modifier la valeur des threads variables et de lecteurs multiples afin de lire la valeur de variable , le modificateur volatile garantit la cohérence de la mémoire.

Si vous avez plusieurs threads pour écrire et lire des variables, le modificateur volatile seul ne garantit pas la cohérence de la mémoire. Vous devez synchronize le code ou utiliser des concepts de concurrence de haut niveau comme les Locks , les Concurrent Collections , Atomic variables etc.

Questions / articles relatifs au SE:

Explication des variables volatiles dans les documents Java

Différence entre volatile et synchronisé en Java

article javarevisited

En fait, je ne suis pas d’accord avec l’exemple donné dans la réponse votée en haut, à ma connaissance, cela ne illustre pas correctement la sémantique volatile selon le modèle de mémoire Java. La volatilité a beaucoup plus de sémantique complexe.

Dans l’exemple fourni, le thread principal pourrait continuer à imprimer “La température d’aujourd’hui est à 0” pour toujours, même si un autre thread est en cours d’exécution et qu’il est supposé mettre à jour la température si cet autre thread n’est jamais planifié.

Une meilleure façon d’illustrer la sémantique volatile est d’utiliser 2 variables.

Pour simplifier, nous supposerons que la méthode “setTemperatures” est la seule façon de mettre à jour les deux variables.

Pour simplifier, nous supposerons que seuls 2 threads sont en cours d’exécution, thread principal et thread 2.

 //volatile variable private static volatile int temperature; //any other variable, could be volatile or not volatile doesnt matter. private static int yesterdaysTemperature //Called by other thread(s) public static void setTemperatures(int temp, int yestemp){ //thread updates yesterday's temperature yesterdaysTemperature = yestemp; //thread updates today's temperature. //This instruction can NOT be moved above the previous instruction for optimization. temperature = temp; } 

les deux dernières instructions d’affectation ne peuvent PAS être réorganisées à des fins d’optimisation par le compilateur, le runtime ou le matériel.

 public static void main(Ssortingng[] args) throws Exception{ while(true){ Thread.sleep(2000); System.out.println("Today's temperature is "+temperature); System.out.println("Yesterday's temperature was "+yesterdaysTemperature ); } } 

Une fois que le thread principal lit la température variable volatile (en cours d’impression),

1) Il existe une garantie qu’il verra la valeur écrite la plus récente de cette variable volatile, quel que soit le nombre de threads qui y écrivent, quelle que soit la méthode mise à jour, synchronisée ou non.

2) Si l’instruction system.out dans le thread principal s’exécute, après l’instant où le thread 2 a exécuté l’instruction temperature = temp, la température d’hier et la température d’aujourd’hui seront garanties pour imprimer les valeurs définies par le thread 2 lorsque il a couru la déclaration température = temp.

Cette situation devient LOT plus complexe si a) Plusieurs threads sont en cours d’exécution et b) Il existe d’autres méthodes que la méthode setTemperatures qui peut mettre à jour la température d’hier et la température d’hier appelées par ces autres threads. Je pense qu’il faudrait un article de taille décente pour parsingr les implications en fonction de la manière dont le modèle de mémoire Java décrit la sémantique volatile.

En bref, essayer de simplement utiliser volatile pour la synchronisation est extrêmement risqué, et vous feriez mieux de vous en tenir à la synchronisation de vos méthodes.

http://mindprod.com/jgloss/volatile.html

“Le mot clé volatile est utilisé sur les variables pouvant être modifiées simultanément par d’autres threads.”

“Comme les autres threads ne peuvent pas voir les variables locales, il n’est jamais nécessaire de marquer les variables locales volatiles. Vous devez synchroniser pour coordonner les modifications apscopes aux variables de différents threads, mais souvent volatiles ne feront que les regarder.”

La valeur de cette variable ne sera jamais mise en cache localement: toutes les lectures et écritures iront directement à la “mémoire principale”. En d’autres termes, le compilateur Java et Thread qui ne mettent pas la valeur de cette variable en cache et le lisent toujours il de la mémoire principale.