Avez-vous déjà utilisé le mot-clé volatile en Java?

au travail aujourd'hui, j'ai trouvé le mot-clé volatile en Java. N'étant pas familier avec elle, j'ai trouvé cette explication:

Java la théorie et la pratique: la Gestion de la volatilité

étant donné le détail dans lequel cet article explique le mot-clé en question, l'utilisez-vous jamais ou Pourriez-vous jamais voir un cas dans lequel vous pourriez utiliser ce mot-clé de la bonne manière?

532
demandé sur Daniel Werner 2008-09-20 04:41:02

21 réponses

volatile a une sémantique pour la visibilité de mémoire. Fondamentalement, la valeur d'un champ volatile devient visible pour tous les lecteurs (en particulier les autres threads) après qu'une opération d'écriture soit terminée. Sans volatile , les lecteurs pourraient voir une certaine valeur non mise à jour.

pour répondre à votre question: Oui, j'utilise une variable volatile pour contrôler si un code continue une boucle. La boucle teste la valeur volatile et continue si elle est true . Le la condition peut être définie à false en appelant une méthode" stop". La boucle voit false et se termine lorsqu'elle teste la valeur après que la méthode d'arrêt ait terminé l'exécution.

Le livre", Java Simultanéité dans la Pratique ," que je recommande fortement, donne une bonne explication de volatile . Ce livre est écrit par la même personne qui a écrit le IBM article est référencé dans la question (en fait, il cite son livre au bas de la article.) Mon utilisation de volatile est ce que son article appelle le "modèle 1 indicateur d'état."

si vous voulez en savoir plus sur la façon dont volatile fonctionne sous le capot, lire le modèle de mémoire Java . Si vous voulez aller au-delà de ce niveau, consultez un bon livre d'architecture informatique comme Hennessy & Patterson et lisez sur la cohérence de cache et la cohérence de cache.

632
répondu Greg Mattes 2017-05-11 07:44:40

" ... le modificateur volatile garantit que tout fil qui lit un champ verra la valeur la plus récente écrite." - Josh Bloch



Si vous pensez utiliser volatile , lisez sur le paquet java.util.concurrent qui traite du comportement atomique.



Le billet Wikipedia sur un Singleton Le modèle montre une utilisation volatile.

148
répondu Andrew Turner 2017-05-11 07:43:16

point Important à propos de volatile :

  1. la synchronisation en Java est possible en utilisant les mots clés Java synchronized et volatile et les serrures.
  2. en Java, nous ne pouvons pas avoir la variable synchronized . L'utilisation du mot-clé synchronized avec une variable est illégale et entraînera une erreur de compilation. Au lieu d'utiliser la variable synchronized en Java, vous pouvez utiliser la variable volatile en java., qui donnera l'instruction aux threads JVM de lire la valeur de la variable volatile depuis la mémoire principale et de ne pas la mettre en cache localement.
  3. Si une variable n'est pas partagée entre plusieurs threads alors il n'est pas nécessaire d'utiliser le volatile mot-clé.

source

exemple d'utilisation de volatile :

public class Singleton {
    private static volatile Singleton _instance; // volatile variable
    public static Singleton getInstance() {
        if (_instance == null) {
            synchronized (Singleton.class) {
                if (_instance == null)
                    _instance = new Singleton();
            }
        }
        return _instance;
    }
}

nous créons instance paresseusement au moment de la première demande.

si nous ne faisons pas la _instance variable volatile alors le fil qui crée l'instance de Singleton n'est pas capable de communiquer à l'autre fil. Donc si Thread a crée une instance Singleton et juste après la création, le CPU corrompt etc, tous les autres threads ne pourront pas voir la valeur de _instance comme non nulle et ils croiront qu'elle est toujours assignée nulle.

pourquoi cela arrive-t-il? Parce que les threads de lecteur ne font pas de verrouillage et jusqu'à ce que le thread d'auteur sorte d'un bloc synchronisé, la mémoire ne sera pas synchronisée et la valeur de _instance ne sera pas mise à jour dans la mémoire principale. Avec le mot-clé Volatile en Java, cela est géré par Java lui-même et de telles mises à jour seront visibles par tous les threads de lecteur.

Conclusion : volatile le mot-clé est également utilisé pour communiquer le contenu de la mémoire entre les fils.

Exemple d'utilisation de sans volatile:

public class Singleton{    
    private static Singleton _instance;   //without volatile variable
    public static Singleton getInstance(){   
          if(_instance == null){  
              synchronized(Singleton.class){  
               if(_instance == null) _instance = new Singleton(); 
      } 
     }   
    return _instance;  
    }

le code ci-dessus n'est pas sûr. Bien qu'il vérifie encore une fois la valeur de l'instance dans le bloc synchronisé (pour des raisons de performance), le compilateur JIT peut réorganiser le bytecode de manière à ce que la référence à l'instance soit réglée avant que le constructeur ait terminé son exécution. Ce signifie que la méthode gettinstance () renvoie un objet qui n'a peut-être pas été initialisé complètement. Pour rendre le thread-safe, le mot-clé volatile peut être utilisé depuis Java 5 pour la variable d'instance. Les Variables qui sont marquées comme volatiles ne sont visibles aux autres threads qu'une fois que le constructeur de l'objet a terminé son exécution complètement.

Source

enter image description here

volatile utilisation en Java :

les itérateurs rapides sont typiquement implémentés en utilisant un compteur volatile sur l'objet list.

  • Lorsque la liste est mise à jour, le compteur est incrémenté.
  • Lorsqu'un Iterator est créé, la valeur courante du compteur est intégrée à l'objet Iterator .
  • Lorsqu'une opération Iterator est effectuée, la méthode compare les deux valeurs de compteur et lance un ConcurrentModificationException si elles sont différentes.

la mise en œuvre d'itérateurs à sécurité intégrée est généralement légère. Ils s'appuient généralement sur les propriétés des structures de données de l'implémentation de la liste spécifique. Il n'existe pas de modèle général.

83
répondu Premraj 2018-06-26 15:45:27

volatile est très utile pour arrêter les threads.

non pas que vous devriez écrire vos propres threads, Java 1.6 a beaucoup de belles piscines de threads. Mais si vous êtes sûr que vous avez besoin d'un fil, vous aurez besoin de savoir comment l'arrêter.

le modèle que j'utilise pour les fils est:

public class Foo extends Thread {
  private volatile boolean close = false;
  public void run() {
    while(!close) {
      // do work
    }
  }
  public void close() {
    close = true;
    // interrupt here if needed
  }
}

Remarquez comment il n'est pas nécessaire pour la synchronisation

44
répondu Pyrolistical 2008-09-24 22:29:39

un exemple courant pour utiliser volatile est d'utiliser une variable volatile boolean comme un drapeau pour terminer un thread. Si vous avez démarré un thread, et que vous voulez être en mesure de l'interrompre en toute sécurité à partir d'un thread différent, vous pouvez avoir le thread vérifier périodiquement un drapeau. Pour l'arrêter, mettez le drapeau à true. En faisant le drapeau volatile , vous pouvez vous assurer que le thread qui le vérifie verra qu'il a été réglé la prochaine fois qu'il le vérifie sans même avoir à utiliser un synchronized bloc.

28
répondu Dave L. 2008-09-20 04:00:01

Oui, volatile doit être utilisé chaque fois que vous voulez qu'une variable mutable soit accessible par plusieurs threads. Il n'est pas très commun usecase parce que généralement vous avez besoin d'effectuer plus d'une opération atomique simple (par exemple vérifier l'état de la variable avant de la modifier), dans ce cas, vous utiliserez un bloc synchronisé à la place.

12
répondu ykaganovich 2008-09-20 04:26:30

personne n'a mentionné le traitement de l'opération de lecture et d'écriture pour le type long et double variable. Les lectures et les Écritures sont des opérations atomiques pour les variables de référence et pour la plupart des variables primitives, sauf pour les types de variables longues et doubles, qui doivent utiliser le mot-clé volatile pour être des opérations atomiques. @link

12
répondu Donatello Boccaforno 2015-02-11 14:51:29

IMO deux scénarios importants autres que le fil d'arrêt dans lequel le mot-clé volatile est utilisé sont

  1. double-check locking mechanism . Utilisé souvent dans le design de Singleton modèle. En cela le singleton object needs to be declared volatile .
  2. Fausse Réveils . Thread peut parfois se réveiller d'attente d'appel, même si aucune informer appel a été émis. Ce comportement est appelé réveil superflu. Ceci peut être contré par en utilisant une variable conditionnelle (drapeau booléen). Mettre le wait() dans une boucle while tant que le drapeau est vrai. Ainsi, si thread se réveille d'un appel d'attente pour des raisons autres que notify/notifyall, alors il rencontre le drapeau est toujours vrai et donc les appels attendent à nouveau. Avant d'appeler notify mettre ce drapeau à true. Dans ce cas, le boolean flag is declared as volatile .
9
répondu Aniket Thakur 2014-02-25 09:17:27

une variable déclarée avec le mot-clé volatile , a deux qualités principales qui la rendent spéciale.

  1. si nous avons une variable volatile, elle ne peut être mise en cache dans la mémoire cache de l'ordinateur(microprocesseur) par aucun fil. L'accès se fait toujours depuis la mémoire principale.

  2. S'il y a une "write operation allant sur une variable volatile, et tout à coup un opération de lecture est demandé, il est garanti que la opération d'écriture sera terminé avant l'opération de lecture .

deux des qualités ci-dessus déduisent que

  • tous les threads lisant une variable volatile liront certainement la dernière valeur. Parce qu'aucune valeur en cache ne peut la polluer. Et aussi la demande de lecture ne sera accordée qu'après la fin de la présente opération d'écriture.

et d'autre part,

  • si nous examinons plus en détail le #2 que j'ai mentionné, nous pouvons voir que volatile mot-clé est un moyen idéal pour maintenir une variable partagée qui a 'n' nombre de fils de lecture et un seul fil d'écriture pour y accéder. Une fois que nous ajoutons le mot-clé volatile , il est fait. Pas d'autres frais généraux sur la sécurité des fils.

Inversement,

nous can't utilisez volatile mot-clé uniquement, pour satisfaire une variable partagée qui a plus d'un fil d'écriture y accéder .

8
répondu Supun Wijerathne 2018-06-23 02:28:48

vous aurez besoin d'utiliser le mot-clé "volatile", ou "synchronisé" et tout autre outil de contrôle de la concurrence et des techniques que vous pourriez avoir à votre disposition si vous développez une application multithread. Les applications bureautiques sont un exemple d'application de ce type.

si vous développez une application qui serait déployée sur le serveur d'application (Tomcat, JBoss AS, Glassfish, etc) vous n'avez pas à gérer vous-même le contrôle de la concurrence comme il est déjà traité par l'application serveur. En fait, si je me souviens bien, la norme Java EE interdit tout contrôle de la concurrence dans les servlets et les EJBs, car elle fait partie de la couche "infrastructure" que vous êtes censé être libéré de la manipulation. Vous ne faites le contrôle de la simultanéité dans une telle application que si vous implémentez des objets singleton. Cela même déjà abordé si vous tricotez vos composants en utilisant cadrorkd comme le ressort.

Donc, dans la plupart des cas de développement Java où l'application est une application web et en utilisant le cadre du CIO comme Spring ou EJB, vous n'auriez pas besoin d'utiliser "volatile".

5
répondu Rudi Adianto 2012-07-10 18:02:44

volatile garantit seulement que tous les fils, même eux-mêmes, augmentent. Par exemple: un compteur voit la même face de la variable en même temps. Il n'est pas utilisé à la place de synchronisé ou atomique ou d'autres choses, il rend complètement les lectures synchronisées. S'il vous plaît ne pas le comparer avec d'autres mots-clés java. Comme le montre l'exemple ci-dessous, les opérations variables volatiles sont aussi atomiques, elles échouent ou réussissent immédiatement.

package io.netty.example.telnet;

import java.util.ArrayList;
import java.util.List;

public class Main {

    public static volatile  int a = 0;
    public static void main(String args[]) throws InterruptedException{

        List<Thread> list = new  ArrayList<Thread>();
        for(int i = 0 ; i<11 ;i++){
            list.add(new Pojo());
        }

        for (Thread thread : list) {
            thread.start();
        }

        Thread.sleep(20000);
        System.out.println(a);
    }
}
class Pojo extends Thread{
    int a = 10001;
    public void run() {
        while(a-->0){
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Main.a++;
            System.out.println("a = "+Main.a);
        }
    }
}

même vous avez mis volatile ou pas, les résultats seront toujours différentes. Mais si vous utilisez AtomicInteger ci-dessous les résultats seront toujours les mêmes. C'est même avec synchronisée aussi.

    package io.netty.example.telnet;

    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.atomic.AtomicInteger;

    public class Main {

        public static volatile  AtomicInteger a = new AtomicInteger(0);
        public static void main(String args[]) throws InterruptedException{

            List<Thread> list = new  ArrayList<Thread>();
            for(int i = 0 ; i<11 ;i++){
                list.add(new Pojo());
            }

            for (Thread thread : list) {
                thread.start();
            }

            Thread.sleep(20000);
            System.out.println(a.get());

        }
    }
    class Pojo extends Thread{
        int a = 10001;
        public void run() {
            while(a-->0){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Main.a.incrementAndGet();
                System.out.println("a = "+Main.a);
            }
        }
    }
5
répondu fatih tekin 2016-08-18 15:06:14

Oui, je l'utilise beaucoup - il peut être très utile pour le code multi-threadé. L'article que vous avez évoqué est un bon. Bien qu'il y ait deux choses importantes à garder à l'esprit:

  1. vous ne devez utiliser volatile que si vous: comprendre complètement ce qu'il fait et en quoi ça diffère de synchronisé. Dans de nombreuses situations, une certaine volatilité apparaît, sur la surface, pour être plus simple, plus performant alternative à synchronisé, quand souvent un meilleur compréhension de la volatilité ferait il est clair que synchronisé est le seul la possibilité de travailler.
  2. volatile ne fait pas travailler dans un de nombreuses JVM plus anciennes, bien que synchronisé. Je me souviens avoir vu un document qui faisait référence aux différents niveaux de soutien dans les différentes JVM, mais je ne le trouve malheureusement pas maintenant. Regardez bien si vous utilisez Java pre 1.5 ou si vous n'avez pas le contrôle des JVM sur lesquelles votre programme fonctionnera.
4
répondu MB. 2008-09-20 11:07:48

Absolument, oui. (Et pas seulement en Java, mais aussi en C#.) Il y a des moments où vous avez besoin d'obtenir ou de définir une valeur qui est garantie d'être une opération atomique sur votre plate-forme donnée, un int ou booléen, par exemple, mais ne nécessitent pas la hauteur de verrouillage du filetage. Le mot-clé volatile vous permet de vous assurer que lorsque vous lisez la valeur que vous obtenez la valeur courante et non une valeur mise en cache qui a été juste rendue obsolète par une écriture sur un autre thread.

3
répondu dgvid 2012-01-06 14:28:48

chaque thread accédant à un champ volatile Lira sa valeur actuelle avant de continuer, au lieu d'utiliser (potentiellement) une valeur en cache.

seule la variable membre peut être volatile ou transitoire.

3
répondu tstuber 2014-08-11 16:27:12

il y a deux utilisations différentes du mot-clé volatile.

  1. empêche JVM de lire les valeurs de register (supposons qu'il s'agit d'un cache), et force sa valeur à être lue de mémoire.
  2. réduit le risque d'erreurs de cohérence de mémoire.

empêche JVM de lire les valeurs dans le registre, et force son lire la valeur de la mémoire.

Un busy flag est utilisé pour empêcher un thread de continuer alors que l'appareil est occupé et le drapeau n'est pas protégé par une serrure:

while (busy) {
    /* do something else */
}

le thread de test se poursuivra lorsqu'un autre thread désactivera le busy flag :

busy = 0;

cependant, comme on accède fréquemment à busy dans le thread de test, la JVM peut optimiser le test en plaçant la valeur de busy dans un registre, puis tester le contenu du enregistrez sans lire la valeur de busy in memory avant chaque test. Le thread de test ne verrait jamais de changement de busy et l'autre thread ne ferait que changer la valeur de busy en mémoire, ce qui aboutirait à une impasse. Déclarer le busy flag comme étant volatile force sa valeur à être lue avant chaque test.

réduit le risque d'erreurs de cohérence de la mémoire.

utilisant des variables volatiles réduit le risque de erreurs de consistance de mémoire , parce que toute écriture à une variable volatile établit "passe-avant" relation avec les lectures suivantes de cette même variable. Cela signifie que les changements à une variable volatile sont toujours visibles aux autres threads.

la technique de la lecture, l'écriture sans erreurs de consistance de mémoire s'appelle action atomique .

une action atomique est une cela se produit effectivement tout d'un coup. Une action atomique ne peut pas s'arrêter au milieu: soit elle se produit complètement, soit elle ne se produit pas du tout. Aucun effet secondaire d'une action atomique n'est visible tant que l'action n'est pas terminée.

ci-dessous sont des actions que vous pouvez spécifier qui sont atomiques:

  • lit et écrit sont atomiques pour les variables de référence et pour la plupart variables primitives (tous les types sauf long et double).
  • se lit comme suit: les Écritures sont atomiques pour toutes les variables déclarées volatile (y compris les variables longues et doubles).

santé!

2
répondu dheeran 2017-03-02 08:42:57

les Variables volatiles sont la synchronisation du poids léger. Lorsque la visibilité des dernières données parmi tous les threads est nécessaire et que l'atomicité peut être compromise , dans de telles situations, les Variables volatiles doivent être préférées. Les variables volatiles renvoient toujours les Écritures les plus récentes faites par n'importe quel thread car elles ne sont pas mises en cache dans les registres ni dans les caches où les autres processeurs ne peuvent pas les voir. Volatile est sans Verrouillage. J'utilise volatile, quand le scénario répond aux critères mentionnés ci-dessus.

2
répondu Neha Vari 2018-02-06 12:57:06

From oracle documentation page , le besoin de variable volatile se pose pour corriger les problèmes de cohérence de la mémoire:

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 les lectures subséquentes de cette même variable.

cela signifie que les changements à une variable volatile sont toujours visible aux autres fils. Cela signifie également que lorsqu'un thread lit une variable volatile, il ne voit pas seulement le dernier changement au volatile , mais aussi les effets secondaires du code qui a conduit au changement.

comme expliqué dans Peter Parker réponse, en l'absence de volatile modificateur, la pile de chaque thread peut avoir leur propre copie de variable. En faisant la variable volatile , les problèmes de cohérence de la mémoire ont été corrigés.

regardez jenkov tutoriel de la page pour une meilleure compréhension.

regardez liées SE question pour plus de détails sur volatils et d'utilisation les cas d'utilisation de volatile:

différence entre volatile et synchronisé en Java

Un cas d'utilisation:

vous avez de nombreux threads, qui ont besoin d'imprimer l'heure actuelle dans un format particulier par exemple: java.text.SimpleDateFormat("HH-mm-ss") . Yon peut avoir une classe, qui convertit le temps courant en SimpleDateFormat et mis à jour la variable pour chaque seconde. Tous les autres threads peuvent simplement utiliser cette variable volatile pour imprimer le temps courant dans les fichiers journaux.

1
répondu Ravindra babu 2017-05-23 11:55:13

A variable Volatile est modifié de façon asynchrone par l'exécution simultanée de threads dans une application Java. Il n'est pas permis d'avoir une copie locale d'une variable qui est différente de la valeur actuellement détenus dans des "principaux" de la mémoire. Effectivement, une variable déclarée volatile doit avoir ses données synchronisées à travers tous les threads, de sorte que chaque fois que vous accédez ou mettez à jour la variable dans un thread, tous les autres threads voient immédiatement la même valeur. Bien sûr, il est probable que les variables volatiles ont un accès plus élevé et mettre à jour les frais généraux que les variables "simples", puisque les threads de raison peuvent avoir leur propre copie de données est pour une meilleure efficacité.

Lorsqu'un champ est déclaré volatile, le compilateur et l'exécution sont avertis que cette variable est partagée et que les opérations sur elle ne doivent pas être réordonnées avec d'autres opérations de mémoire.Les variables volatiles ne sont pas mises en cache dans les registres ou dans les caches où elles sont cachées d'autres processeurs, donc une lecture d'une variable volatile renvoie toujours la plus récente écrire par n'importe quel thread.

pour référence, référez-vous à ce http://techno-terminal.blogspot.in/2015/11/what-are-volatile-variables.html

0
répondu satish 2015-11-17 09:36:43

la touche volatile lorsqu'elle est utilisée avec une variable, garantit que les threads qui lisent cette variable verront la même valeur . Maintenant, si vous avez plusieurs fils de lecture et d'écriture à une variable, rendre la variable volatile ne sera pas suffisant et les données seront corrompus . Les threads d'Image ont lu la même valeur mais chacun a fait quelques chages (disons incrémenté un compteur) , en écrivant de nouveau à la mémoire, l'intégrité des données est violée . C'est pourquoi il est nécessaire de faire varier synchronisé (des voies différentes sont possibles)

si les changements sont faits par 1 thread et que les autres ont juste besoin de lire cette valeur, le volatile sera approprié.

0
répondu Java Main 2015-12-24 16:41:21

Volatile suit.

1 > Lire et écrire des variables volatiles par différents threads sont toujours à partir de la mémoire, pas à partir de la propre cache du thread ou du registre cpu. Ainsi chaque fil traite toujours avec la dernière valeur. 2> Lorsque 2 threads différents fonctionnent avec la même instance ou des variables statiques dans le tas, l'un peut voir les actions de l'autre comme hors de l'ordre. Voir le blog de jeremy manson à ce sujet. Mais volatile aide ici.

qui suit le code complet montre comment un certain nombre de threads peuvent être exécutés dans un ordre prédéfini et imprimer des sorties sans utiliser le mot-clé synchronisé.

thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3
thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3
thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3

pour ce faire, nous pouvons utiliser le code de course complet suivant.

public class Solution {
    static volatile int counter = 0;
    static int print = 0;
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Thread[] ths = new Thread[4];
        for (int i = 0; i < ths.length; i++) {
            ths[i] = new Thread(new MyRunnable(i, ths.length));
            ths[i].start();
        }
    }
    static class MyRunnable implements Runnable {
        final int thID;
        final int total;
        public MyRunnable(int id, int total) {
            thID = id;
            this.total = total;
        }
        @Override
        public void run() {
            // TODO Auto-generated method stub
            while (true) {
                if (thID == counter) {
                    System.out.println("thread " + thID + " prints " + print);
                    print++;
                    if (print == total)
                        print = 0;
                    counter++;
                    if (counter == total)
                        counter = 0;
                } else {
                    try {
                        Thread.sleep(30);
                    } catch (InterruptedException e) {
                        // log it
                    }
                }
            }
        }
    }
}

le lien github suivant a un readme, qui donne une explication correcte. https://github.com/sankar4git/volatile_thread_ordering

0
répondu sankar banerjee 2018-05-31 06:23:43

j'aime jenkov explication

le mot-clé Java volatile est utilisé pour marquer une variable Java comme étant"stockée dans la mémoire principale". Plus précisément, cela signifie que chaque lecture d'une variable volatile sera lue à partir de la mémoire principale de l'ordinateur, et non à partir du cache CPU, et que chaque écriture à une variable volatile sera écrite à la mémoire principale, et pas seulement au cache CPU.

En fait, depuis Java 5 le mot-clé volatile garantit plus que simplement que les variables volatiles sont écrites et lues à partir de la mémoire principale. Il s'agit de la garantie de visibilité étendue "happens-before".

Considérations sur la Performance de la volatilité

la lecture et l'écriture de variables volatiles provoque la variable à être lu ou écrit à la mémoire principale. Lire et écrire à partir de la mémoire principale est plus cher que accéder à la cache du PROCESSEUR. L'accès aux variables volatiles empêche également la réorganisation de l'instruction, qui est une technique normale d'amélioration de la performance. Ainsi, vous ne devez utiliser des variables volatiles que lorsque vous avez vraiment besoin de renforcer la visibilité des variables.

0
répondu yoAlex5 2018-06-16 14:16:55