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

j'ai lu " quand utiliser" volatile " en Java? " mais je suis encore confus. Comment savoir si je dois marquer une variable volatile? Et si je me trompe, soit en omettant une substance volatile sur quelque chose qui en a besoin, soit en mettant une substance volatile sur quelque chose qui n'en a pas besoin? Quelles sont les règles empiriques pour déterminer quelles variables devraient être volatiles dans le code multithread?

79
demandé sur Ravindra babu 2010-08-15 22:36:16

7 réponses

vous l'utilisez essentiellement quand vous voulez laisser une variable membre être accessible par plusieurs threads, mais n'avez pas besoin atomicité composé (pas sûr 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, parce que vous besoin atomicité composé.

 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(String[] args) throws Exception{
        while(true){
           Thread.sleep(2000);
           System.out.println("Today's temperature is "+temperature);
        }
    }
}

maintenant, pourquoi ne pas utiliser private static int temperature ? En fait, vous pouvez (en le sens que votre programme ne va pas exploser ou quelque chose), mais le changement de temperature par l'autre fil peut ou ne peut pas être "visible" par le fil principal.

cela signifie essentiellement qu'il est même possible que votre application. continue d'écrire Today's temperature is 0 pour toujours si vous don't utilisez volatile (dans la pratique, la valeur tend à devenir éventuellement visible. Cependant, vous ne devez pas prendre le risque de ne pas utiliser volatile si nécessaire, car il peut conduire à les bogues désagréables (causés par des objets entièrement construits, etc.)).

si vous mettez volatile mot-clé sur quelque chose qui n'a pas besoin volatile , cela n'affectera pas l'exactitude de votre code (c.-à-d. le comportement ne changera pas). En termes de performances, il dépendra de la JVM. En théorie, vous pourriez avoir une petite dégradation des performances parce que le compilateur ne peut pas réordonner les optimisations, avoir à invalider le cache CPU, etc., mais encore une fois le compilateur pourrait prouvez que votre champ ne peut jamais être accédé par plusieurs threads et supprimez complètement l'effet du mot-clé volatile et compilez-le en instructions identiques.

EDIT:

Réponse au commentaire:

D'accord, mais pourquoi ne pas synchroniser la température du jour 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 vice versa. Il y a deux raisons pour lesquelles vous pourriez préférer volatile si vous le pouvez:

  1. moins sujet aux bogues: cela dépend du contexte, mais dans de nombreux cas, l'utilisation de volatile est moins sujet aux bogues de concurrence, comme le blocage en tenant la serrure, les blocages, etc.
  2. plus performant: dans la plupart des implémentations JVM, volatile peut avoir débit significativement plus élevé et une meilleure latence. Cependant, dans la plupart des applications, la différence est trop petite à la matière.
97
répondu Enno Shioji 2014-07-22 07:11:00

Volatile est le plus utile dans les algorithmes sans verrouillage. Vous marquez la variable contenant des données partagées comme volatile lorsque vous n'utilisez pas le verrouillage pour accéder à cette variable et que vous voulez que les modifications apportées par un thread soient visibles dans un autre, ou que vous voulez créer une relation "happens-after" Pour vous assurer que le calcul n'est pas réordonné, encore une fois, pour s'assurer que les modifications deviennent visibles au moment approprié.

Le JMM "livres de cuisine 151940920" décrit les opérations qui peuvent re-commandé et qui ne le peuvent pas.

14
répondu mdma 2010-08-15 18:38:59

le volatile peut également être utilisé pour publier en toute sécurité des objets immuables dans un environnement multi-threadé.

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 Java Simultanéité dans la Pratique pour en savoir plus sur ce sujet.

5
répondu Johannes Wachter 2010-08-15 19:12:58
Le mot-clé

volatile garantit que la valeur de la variable volatile sera toujours lue à partir de la mémoire principale et non à partir du cache local du Thread.

From java competiency tutorial :

L'utilisation de variables volatiles réduit le risque d'erreurs de cohérence de la mémoire, car toute écriture à une variable volatile établit une relation se produit-avant avec les lectures subséquentes de cette même variable

cela signifie que les changements à une variable volatile sont toujours visibles aux autres threads. Cela signifie aussi que lorsqu'un thread lit une variable volatile, il voit non seulement le dernier changement vers la variable volatile, mais aussi les effets secondaires du code qui a conduit au changement.

concernant votre requête:

Comment savoir quand je dois marquer une variable volatile? Quelles sont les règles du pouce lorsque vous déterminez quelles variables devraient être volatiles dans le code multithread?

si vous pensez que tous les threads de lecteur obtiennent toujours la dernière valeur d'une variable, vous devez marquer variable comme volatile

si vous avez un fil d'écriture pour modifier la valeur de la variable et plusieurs fils de lecture pour lire la valeur de la variable , le modificateur volatile garantit la cohérence de la mémoire.

si vous ont plusieurs fils pour écrire et lire des variables, volatile le modificateur seul ne garantit pas la cohérence de la mémoire. Vous devez synchronize le code ou utiliser haut niveau simultanéité constructions comme Locks , Concurrent Collections , Atomic variables etc.

Related SE questions/ articles:

variable Volatile explanation in Java docs

différence entre volatile et synchronisé en Java

javarevisited article

5
répondu Ravindra babu 2017-05-23 12:26:23

en fait en désaccord avec l'exemple donné dans la réponse la plus votée, à ma connaissance, il ne pas correctement illustrer sémantique volatile selon le modèle de mémoire Java. Volatile a une sémantique bien plus complexe.

dans l'exemple fourni, le thread principal pourrait continuer à imprimer" la température D'Aujourd'hui est 0 " pour toujours même s'il y a un autre thread qui est censé mettre à jour la température si cet autre thread n'est jamais programmé.

une meilleure façon d'illustrer la sémantique volatile est avec 2 variables.

par souci de simplicité, nous supposerons que la seule façon de mettre à jour les deux variables est par la méthode " setTemperatures " .

pour des raisons de simplicité, nous supposerons que seulement 2 threads sont en cours d'exécution, le thread principal et le 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 peut PAS être réorganisées pour des raisons d'optimisation par le compilateur, l'exécution ou le matériel.

public static void main(String[] 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 fil principal lit la température variable volatile (en cours d'impression),

1) Il y a une garantie qu'il verra la valeur la plus récente écrite de cette variable volatile quel que soit le nombre de threads qui lui écrivent, quelle que soit la méthode qu'ils mettent à jour en, synchronisés ou non.

2) si le système.l'instruction de sortie dans le fil principal court, après le moment où le fil 2 a couru l'instruction température = temp, la température d'hier et la température d'aujourd'hui sera garanti pour imprimer les valeurs fixées en eux par le fil 2 quand il a couru l'instruction température=temp.

cette situation obtient un LOT plus complexe si a) les fils multiples sont l'exécution et b) Il y a d'autres méthodes que la méthode setTemperatures qui peuvent mettre à jour la variable température d'hier et température d'aujourd'hui qui sont activement appelées par ces autres threads. Je pense qu'il faudrait un article de taille décente pour analyser les implications basées sur la façon dont le modèle de mémoire Java décrit la sémantique volatile.

en bref, essayer de juste utiliser volatile pour la synchronisation est extrêmement risqué, et vous seriez mieux de s'en tenir à synchroniser vos méthodes.

3
répondu programmerravi 2016-01-14 16:46:22

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

"Le mot clé volatile est utilisé sur les variables qui peuvent ê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 avez besoin synchronisé pour coordonner les changements aux variables à partir de différents threads, mais souvent volatile fera juste pour les regarder."

2
répondu Molske 2010-08-15 18:38:17

voltalie signifie Garder la valeur changeante.La valeur de cette variable ne sera jamais mise en cache localement thread-locally: tout ce qui lit et écrit ira directement dans la "mémoire principale".En d'autres termes, le compilateur Java et le Thread qui ne cache pas la valeur de cette variable et toujours le lire à partir de la mémoire principale.

2
répondu Akshay 2018-04-11 05:29:17