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


Au travail aujourd'hui, je suis tombé sur le volatile mot-clé en Java. N'étant pas très familier avec cela, j'ai trouvé cette explication:

Théorie et pratique de Java: Gérer la volatilité

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


516
2017-09-20 00:41


origine


Réponses:


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

Pour répondre à votre question: Oui, j'utilise un volatile variable pour contrôler si un code continue une boucle. La boucle teste le volatile valeur et continue si c'est true. La condition peut être définie sur false en appelant une méthode "stop". La boucle voit false et se termine quand il teste la valeur après l'exécution de la méthode stop.

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

Si vous voulez en savoir plus sur comment volatile travaille sous le capot, lire sur 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 et Patterson et en savoir plus sur la cohérence du cache et la cohérence du cache.


616
2017-09-20 02:09



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

Si vous songez à utiliser volatile, lisez sur l'emballage java.util.concurrent qui traite du comportement atomique.

Le post de Wikipedia sur un Modèle Singleton montre volatile en cours d'utilisation.


144
2017-12-18 21:52



Point important à propos de volatile: 

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

la 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 une instance paresseusement au moment de la première demande.

Si nous ne faisons pas le _instance variable volatile puis le fil qui crée l'instance de Singleton n'est pas capable de communiquer avec l'autre thread. Donc, si Thread A crée une instance de Singleton et juste après sa création, le CPU corrompt, etc., tous les autres threads ne pourront pas voir la valeur de _instancecomme non nul et ils croiront qu'il est toujours assigné nul.

Pourquoi cela arrive-t-il? Étant donné que les threads de lecteur n'effectuent aucun verrouillage et que le thread d'écriture sort d'un bloc synchronisé, la mémoire ne sera pas synchronisée et la valeur de _instance ne sera pas mis à jour dans la mémoire principale. Avec le mot-clé Volatile en Java, ceci est géré par Java lui-même et ces 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 threads.

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 thread-safe. Bien qu'il vérifie à nouveau la valeur de l'instance dans le bloc synchronisé (pour des raisons de performances), le compilateur JIT peut réorganiser le bytecode de telle sorte que la référence à l'instance soit définie avant que le constructeur ait terminé son exécution. Cela signifie que la méthode getInstance () renvoie un objet qui n'a peut-être pas été complètement initialisé. Pour rendre le code thread-safe, le mot-clé volatile peut être utilisé depuis Java 5 pour la variable d'instance. Les variables marquées comme volatiles ne sont visibles que par les autres threads une fois que le constructeur de l'objet a terminé son exécution complètement.
La source

enter image description here

volatile utilisation en Java:

Les itérateurs fail-fast sont typiquement mis en œuvre en utilisant un volatile compteur sur l'objet liste.

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

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


75
2017-09-24 22:29



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

Non que vous devriez écrire vos propres threads, Java 1.6 a beaucoup de beaux pools de threads. Mais si vous êtes sûr que vous avez besoin d'un fil, vous devez savoir comment l'arrêter.

Le modèle que j'utilise pour les threads est le suivant:

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
  }
}

Notez comment il n'y a pas besoin de synchronisation


44
2017-09-20 04:00



Un exemple commun d'utilisation volatile est d'utiliser un volatile boolean variable en tant que drapeau pour terminer un thread. Si vous avez démarré un thread et que vous souhaitez pouvoir l'interrompre en toute sécurité à partir d'un thread différent, vous pouvez demander au thread de vérifier périodiquement un indicateur. Pour l'arrêter, définissez le drapeau sur true. En faisant le drapeau volatile, vous pouvez vous assurer que le thread qui le vérifie verra qu'il a été défini la prochaine fois qu'il le vérifie sans même avoir à utiliser un synchronized bloc.


28
2017-09-20 04:26



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 courant parce que, typiquement, vous devez effectuer plus d'une opération atomique (par exemple vérifier l'état de la variable avant de la modifier), auquel cas vous utiliserez un bloc synchronisé à la place.


12
2018-02-11 14:51



Personne n'a mentionné le traitement des opérations de lecture et d'écriture pour les variables longues et doubles. 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 longs et doubles, qui doivent utiliser le mot clé volatile pour être des opérations atomiques. @lien


12
2018-02-25 09:17



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

  1. Mécanisme de verrouillage à double vérification. Utilisé souvent dans la conception de Singleton modèle. En cela, le singleton object needs to be declared volatile.
  2. Wakeups Spurious. Le fil peut parfois se réveiller à partir d'un appel en attente même si aucun appel de notification n'a été émis. Ce comportement est appelé réveil supurious. Cela peut être contré en utilisant une variable conditionnelle (drapeau booléen). Placez l'appel wait () dans une boucle while tant que l'indicateur est vrai. Donc, si le thread se réveille de l'appel d'attente pour des raisons autres que notify / notifyall alors il rencontre le drapeau est toujours vrai et par conséquent les appels attendent à nouveau. Avant d'appeler notify, définissez ce drapeau sur true. Dans ce cas, le boolean flag is declared as volatile.

9
2017-09-20 07:14



Vous devrez utiliser des mots clés «volatiles» ou «synchronisés», ainsi que tous les autres outils et techniques de contrôle de la simultanéité dont vous disposez si vous développez une application multithread. Exemple d'une telle application est des applications de bureau.

Si vous développez une application qui serait déployée sur un serveur d'applications (Tomcat, JBoss AS, Glassfish, etc.), vous n'avez pas à gérer le contrôle de simultanéité tel qu'il est déjà traité par le serveur d'applications. En fait, si je me souvenais bien, la norme Java EE interdisait tout contrôle de simultanéité dans les servlets et les EJB, car cela faisait partie de la couche «infrastructure» que vous deviez libérer du traitement. Vous ne contrôlez la concurrence que dans une telle application si vous implémentez des objets singleton. Cela a même déjà répondu si vous tricotez vos composants en utilisant frameworkd comme Spring.

Ainsi, dans la plupart des cas de développement Java où l'application est une application Web et utilise un framework IoC comme Spring ou EJB, vous n'avez pas besoin d'utiliser 'volatile'.


5
2018-03-02 21:07



volatile seulement garantit que tous les threads, même eux-mêmes, s'incrémentent. Par exemple: un compteur voit la même face de la variable en même temps. Il n'est pas utilisé au lieu de synchronisé ou atomique ou autre chose, 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 de variables volatiles sont également atomiques, elles échouent ou réussissent à la fois.

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 si vous mettez volatile ou non, les résultats seront toujours différents. Mais si vous utilisez AtomicInteger comme ci-dessous les résultats seront toujours les mêmes. C'est pareil avec synchronisé 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
2018-03-04 18:24