À quoi sert le mot clé volatile

672

Au travail aujourd'hui, je suis tombé sur le volatilemot - clé en Java. Ne le connaissant pas très bien, j'ai trouvé cette explication:

Théorie et pratique Java: gestion de la volatilité

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

Richard
la source

Réponses:

742

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

Pour répondre à votre question: Oui, j'utilise une volatilevariable pour contrôler si du code continue une boucle. La boucle teste la volatilevaleur et continue si elle l'est true. La condition peut être définie sur falseen appelant une méthode "stop". La boucle voit falseet se termine lorsqu'elle teste la valeur une fois la méthode d'arrêt terminée.

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

Si vous souhaitez en savoir plus sur le volatilefonctionnement sous le capot, lisez 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 et la cohérence du cache.

Greg Mattes
la source
118
Cette réponse est correcte, mais incomplète. Il omet une propriété importante de volatilecelle fournie avec le nouveau modèle de mémoire Java défini dans JSR 133: que lorsqu'un thread lit une volatilevariable, il voit non seulement la dernière valeur qui lui a été écrite par un autre thread, mais aussi toutes les autres écritures sur d'autres variables qui étaient visibles dans cet autre fil au moment de l' volatileécriture. Voir cette réponse et cette référence .
Adam Zalcman
46
Pour les débutants, je vous demanderais de démontrer avec du code (s'il vous plaît?)
Hungry Blue Dev
6
L'article lié dans la question contient des exemples de code.
Greg Mattes
Je pense que le lien «Hennessy & Patterson» est rompu. Et le lien vers «le modèle de mémoire Java» mène en fait au chapitre 17 de la spécification du langage Java d'Oracle «Threads and Locks».
Kris
2
@fefrei: «immédiatement» est un terme familier. Bien sûr, cela ne peut pas être garanti lorsque ni le timing d'exécution ni les algorithmes de programmation des threads ne sont réellement spécifiés. La seule façon pour un programme de savoir si une lecture volatile est postérieure à une écriture volatile particulière, est de vérifier si la valeur vue est la valeur écrite attendue.
Holger
177

«… Le modificateur volatile garantit que tout thread qui lit un champ verra la dernière valeur écrite.» - Josh Bloch

Si vous songez à utiliser volatile, lisez le paquet java.util.concurrentqui traite du comportement atomique.

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

Ande TURNER
la source
18
Pourquoi y a-t-il à la fois volatileet des synchronizedmots clés?
ptkato
5
L'article Wikipédia sur un motif Singleton a beaucoup changé depuis et ne présente plus ledit volatileexemple. Il peut être trouvé dans une version archivée .
bskp
1
@ptkato Ces deux mots clés ont des objectifs complètement différents, donc la question n'a pas beaucoup de sens en tant que comparaison, bien qu'ils soient tous deux liés à la concurrence. C'est comme dire "Pourquoi y a-t-il à la fois des mots void- publicclés".
DavidS
134

Point important sur volatile:

  1. La synchronisation en Java est possible en utilisant des mots - clés Java synchronizedet volatileet serrures.
  2. En Java, nous ne pouvons pas avoir de synchronizedvariable. L'utilisation d'un synchronizedmot clé avec une variable est illégale et entraînera une erreur de compilation. Au lieu d'utiliser la synchronizedvariable en Java, vous pouvez utiliser la volatilevariable java , qui demandera aux threads JVM de lire la valeur de la volatilevariable dans 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, il n'est pas nécessaire d'utiliser le volatilemot 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 la _instancevariable, volatilele thread qui crée l'instance de Singletonn'est pas en mesure de communiquer avec l'autre thread. Donc, si le thread A crée une instance 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 attribué null.

Pourquoi cela arrive-t-il? Étant donné que les threads de lecture ne font aucun verrouillage et tant que le thread d'écriture ne sort pas d'un bloc synchronisé, la mémoire ne sera pas synchronisée et la valeur de _instancene 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 ces mises à jour seront visibles par tous les threads de lecture.

Conclusion : le volatilemot-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 manière à ce 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 pour les autres threads une fois que le constructeur de l'objet a complètement terminé son exécution.
La source

entrez la description de l'image ici

volatileutilisation en Java :

Les itérateurs à échec rapide sont généralement implémentés à l'aide d'un volatilecompteur sur l'objet liste.

  • Lorsque la liste est mise à jour, le compteur est incrémenté.
  • Lorsqu'un Iteratorest créé, la valeur actuelle du compteur est incorporée dans l' Iteratorobjet.
  • Lorsqu'une Iteratoropération est effectuée, la méthode compare les deux valeurs de compteur et renvoie a ConcurrentModificationExceptionsi elles sont différentes.

L'implémentation 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 liste spécifique. Il n'y a pas de schéma général.

Premraj
la source
2
"Les itérateurs à échec rapide sont généralement implémentés à l'aide d'un compteur volatile" - ce n'est plus le cas, trop coûteux: bugs.java.com/bugdatabase/view_bug.do?bug_id=6625725
Vsevolod Golovanov
la double vérification de _instance est-elle sûre? je pensais qu'ils ne sont pas sûrs même avec des produits volatils
Dexters
"qui demandera aux threads JVM de lire la valeur de la variable volatile de la mémoire principale et de ne pas la mettre en cache localement." bon point
Humoyun Ahmad
Pour la sécurité des fils, on pourrait aussi y aller private static final Singleton _instance;.
Chris311
53

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

Non pas que vous deviez écrire vos propres threads, Java 1.6 a beaucoup de jolis pools de threads. Mais si vous êtes sûr d'avoir besoin d'un fil, vous devrez savoir comment l'arrêter.

Le modèle que j'utilise pour les threads 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
  }
}

Dans le segment de code ci-dessus, la lecture du thread closedans la boucle while est différente de celle qui appelle close(). Sans volatile, le thread exécutant la boucle peut ne jamais voir le changement se fermer.

Remarquez qu'il n'y a pas besoin de synchronisation

Pyrolistique
la source
2
Je me demande pourquoi c'est même nécessaire. N'est-ce pas seulement nécessaire si d'autres threads doivent réagir au changement d'état de ce thread de telle manière que la synchronisation des threads est en danger?
Jori
27
@Jori, vous avez besoin de volatile car la lecture du thread close dans la boucle while est différente de celle qui appelle close (). Sans volatile, le thread exécutant la boucle peut ne jamais voir le changement se fermer.
Pyrolistical
diriez-vous qu'il y a un avantage à arrêter un thread comme celui-ci ou à utiliser les méthodes Thread # interrupt () et Thread # isInterrupted ()?
Ricardo Belchior
2
@Pyrolistical - Avez-vous observé le fil ne voyant jamais le changement dans la pratique? Ou pouvez-vous étendre l'exemple pour déclencher ce problème de manière fiable? Je suis curieux parce que je sais que j'ai utilisé (et vu d'autres utiliser) du code qui est fondamentalement identique à l'exemple mais sans le volatilemot - clé, et il semble toujours fonctionner correctement.
aroth
2
@aroth: avec les JVM d'aujourd'hui, vous pouvez observer qu'en pratique, même avec les exemples les plus simples, vous ne pouvez pas reproduire ce comportement de manière fiable . Avec des applications plus complexes, vous avez parfois d'autres actions avec des garanties de visibilité de la mémoire dans votre code qui rendent le travail possible, ce qui est particulièrement dangereux car vous ne savez pas pourquoi cela fonctionne et un changement simple et apparemment sans rapport de votre code peut casser votre application…
Holger
31

Un exemple courant d'utilisation volatileconsiste à utiliser une volatile booleanvariable comme indicateur 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 autre thread, vous pouvez demander au thread de vérifier périodiquement un indicateur. Pour l'arrêter, définissez le drapeau sur true. En créant l'indicateur 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érifiera sans même avoir à utiliser un synchronizedbloc.

Dave L.
la source
27

Une variable déclarée avec un volatilemot-clé, a deux qualités principales qui la rendent spéciale.

  1. Si nous avons une variable volatile, elle ne peut pas être mise en cache dans la mémoire cache de l'ordinateur (microprocesseur) par aucun thread. L'accès a toujours eu lieu à partir de la mémoire principale.

  2. S'il y a une opération d'écriture en cours sur une variable volatile et qu'une opération de lecture est soudainement demandée, il est garanti que l' opération d'écriture sera terminée avant l'opération de lecture .

Deux qualités ci-dessus en déduisent que

  • Tous les threads lisant une variable volatile liront certainement la dernière valeur. Parce qu'aucune valeur mise en cache ne peut la polluer. De plus, la demande de lecture ne sera accordée qu'après la fin de l'opération d'écriture en cours.

Et d'autre part,

  • Si nous étudions plus en détail le # 2 que j'ai mentionné, nous pouvons voir que le volatilemot - clé est un moyen idéal pour maintenir une variable partagée qui a 'n' nombre de threads de lecture et un seul thread d'écriture pour y accéder. Une fois le volatilemot clé ajouté , c'est fait. Aucun autre frais généraux sur la sécurité des threads.

Inversement,

Nous ne pouvons pas utiliser le volatilemot clé uniquement pour satisfaire une variable partagée qui a plus d'un thread d'écriture y accédant .

Supun Wijerathne
la source
3
Cela explique la différence entre volatile et synchronisé.
ajay
13

Personne n'a mentionné le traitement des opérations de lecture et d'écriture pour les types de variables longues et doubles. Les lectures et écritures sont des opérations atomiques pour les variables de référence et pour la plupart des variables primitives, à l'exception des types de variables longues et doubles, qui doivent utiliser le mot-clé volatile pour être des opérations atomiques. @lien

Donatello Boccaforno
la source
Pour le rendre encore plus clair, il n'est PAS NÉCESSAIRE de définir un booléen volatile, car la lecture et l'écriture d'un booléen SONT DÉJÀ atomiques.
Kai Wang
2
@KaiWang vous n'avez pas besoin d'utiliser volatile sur les booléens à des fins d'atomicité. Mais vous pourriez certainement le faire pour des raisons de visibilité. C'est ce que tu voulais dire?
SusanW
12

Oui, volatile doit être utilisé chaque fois que vous souhaitez qu'une variable mutable soit accessible par plusieurs threads. Ce n'est pas un cas d'utilisation très courant, car vous devez généralement effectuer plus d'une seule opération atomique (par exemple, vérifier l'état de la variable avant de le modifier), auquel cas vous utiliseriez plutôt un bloc synchronisé.

ykaganovich
la source
10

À mon avis, deux scénarios importants autres que l'arrêt du thread dans lequel un mot-clé volatil est utilisé sont:

  1. Mécanisme de verrouillage à double vérification . Utilisé souvent dans le modèle de conception Singleton. En cela, l' objet singleton doit être déclaré volatile .
  2. Réveils parasites . Le thread peut parfois se réveiller d'un appel en attente même si aucun appel de notification n'a été émis. Ce comportement est appelé réveil parasite. Cela peut être contré en utilisant une variable conditionnelle (drapeau booléen). Mettez l'appel wait () dans une boucle while tant que l'indicateur est vrai. Donc, si le thread se réveille de l'appel en attente pour des raisons autres que Notify / NotifyAll, il rencontre le drapeau est toujours vrai et les appels attendent donc à nouveau. Avant d'appeler notify, réglez cet indicateur sur true. Dans ce cas, le drapeau booléen est déclaré volatile .
Aniket Thakur
la source
Toute la section # 2 semble très confuse, elle confond des notifications perdues, des réveils parasites et des problèmes de visibilité de la mémoire. De plus, si toutes les utilisations de l'indicateur sont synchronisées, la volatilité est redondante. Je pense que je comprends votre argument, mais le réveil parasite n'est pas le terme correct. Précisez s'il vous plaît.
Nathan Hughes
5

Vous devrez utiliser un mot clé «volatile» ou «synchronisé» et tout autre outil et technique de contrôle des accès concurrents que vous pourriez avoir à votre disposition si vous développez une application multithread. Un exemple d'une telle application est les applications de bureau.

Si vous développez une application qui serait déployée sur le serveur d'applications (Tomcat, JBoss AS, Glassfish, etc.), vous n'avez pas à gérer vous-même le contrôle d'accès simultané car il est déjà traité par le serveur d'applications. En fait, si je me souviens bien, la norme Java EE interdit tout contrôle de concurrence dans les servlets et les EJB, car il fait partie de la couche «infrastructure» dont vous êtes censé être libéré. Vous ne contrôlez la concurrence dans une telle application que si vous implémentez des objets singleton. Cela a déjà été résolu 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 utilisant un framework IoC comme Spring ou EJB, vous n'auriez pas besoin d'utiliser 'volatile'.

Rudi Adianto
la source
5

volatilegarantit uniquement que tous les threads, même eux-mêmes, sont incrémentés. Par exemple: un compteur voit la même face de la variable en même temps. Il n'est pas utilisé à la place de choses synchronisées ou atomiques ou autres, il synchronise complètement les lectures. Veuillez 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 des résultats volatils 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 la même chose avec synchronisé également.

    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);
            }
        }
    }
fatih tekin
la source
4

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

  1. Vous ne devez utiliser volatile que si vous comprenez parfaitement ce qu'il fait et comment il diffère de synchronisé. Dans de nombreuses situations, le volatile apparaît, à première vue, comme une alternative plus simple et plus performante au synchronisé, alors que souvent une meilleure compréhension du volatile indique clairement que le synchronisé est la seule option qui fonctionnerait.
  2. volatile ne fonctionne pas dans la plupart des anciennes machines virtuelles Java, bien que synchronisé le fasse. Je me souviens avoir vu un document qui faisait référence aux différents niveaux de support dans différentes machines virtuelles Java, mais malheureusement je ne le trouve pas maintenant. Examinez-le certainement si vous utilisez Java pre 1.5 ou si vous n'avez pas le contrôle sur les machines virtuelles Java sur lesquelles votre programme s'exécutera.
MB.
la source
4

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

Seule la variable membre peut être volatile ou transitoire.

tstuber
la source
3

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

dgvid
la source
3

Il existe deux utilisations différentes du mot-clé volatile.

  1. Empêche la JVM de lire les valeurs du registre (en tant que cache) et force sa valeur à être lue dans la mémoire.
  2. Réduit le risque d'erreurs de cohérence dans la mémoire.

Empêche la machine virtuelle Java de lire des valeurs dans le registre et force sa valeur à être lue dans la mémoire.

Un indicateur occupé est utilisé pour empêcher un thread de continuer pendant que le périphérique est occupé et que l'indicateur n'est pas protégé par un verrou:

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

Le thread de test continuera lorsqu'un autre thread désactivera le drapeau occupé :

busy = 0;

Cependant, comme occupé est fréquemment consulté dans le thread de test, la JVM peut optimiser le test en plaçant la valeur de occupé dans un registre, puis tester le contenu du registre sans lire la valeur de occupé en mémoire avant chaque test. Le thread de test ne verrait jamais de changement d'occupation et l'autre thread ne changerait que la valeur d'occupation en mémoire, ce qui entraînerait un blocage. Déclarer l' indicateur occupé comme volatile force sa valeur à être lue avant chaque test.

Réduit le risque d'erreurs 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 dans une variable volatile établit une relation "se produit avant" avec les lectures suivantes de cette même variable. Cela signifie que les modifications apportées à une variable volatile sont toujours visibles pour les autres threads.

La technique de lecture, d'écriture sans erreurs de cohérence de la mémoire est appelée action atomique .

Une action atomique est une action qui se produit efficacement en même temps. 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.

Vous trouverez ci-dessous des actions atomiques que vous pouvez spécifier:

  • Les lectures et écritures sont atomiques pour les variables de référence et pour la plupart des variables primitives (tous les types sauf long et double).
  • Les lectures et écritures sont atomiques pour toutes les variables déclarées volatiles (y compris les variables longues et doubles).

À votre santé!

Mohanraj Balasubramaniam
la source
3

volatiledit pour un programmeur que la valeur sera toujours à jour. Le problème est que la valeur peut être enregistrée sur différents types de mémoire matérielle. Par exemple, il peut s'agir de registres CPU, cache CPU, RAM ... Les registres CPU et le cache CPU appartiennent au CPU et ne peuvent pas partager de données contrairement à la RAM qui est à la rescousse dans un environnement multithreading

entrez la description de l'image ici

volatileLe mot clé indique qu'une variable sera lue et écrite directement depuis / vers la mémoire RAM . Il a une empreinte de calcul

Java 5étendu volatileen soutenant happens-before[À propos]

Une écriture dans un champ volatile se produit avant chaque lecture ultérieure de ce champ.

volatileLe mot clé ne résout pas une race conditionsituation où plusieurs threads peuvent écrire simultanément certaines valeurs. La réponse est le synchronizedmot clé [À propos]

En conséquence , il sécurité que si un fil écrit et d' autres lisent juste la volatilevaleur

volatile vs synchronisé

yoAlex5
la source
2

Volatile suit.

1> La lecture et l'écriture des variables volatiles par différents threads se font toujours à partir de la mémoire, pas à partir du cache du thread ou du registre CPU. Ainsi, chaque thread traite toujours de la dernière valeur. 2> Lorsque 2 threads différents fonctionnent avec la même instance ou des variables statiques dans le tas, on peut voir les actions des autres comme non ordonnées. Voir le blog de Jeremy Manson à ce sujet. Mais volatile aide ici.

Le code suivant en cours d'exécution montre comment un certain nombre de threads peuvent s'exécuter dans un ordre prédéfini et imprimer des sorties sans utiliser de 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 fonctionnement 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 appropriée. https://github.com/sankar4git/volatile_thread_ordering

sankar banerjee
la source
1

Depuis la page de documentation d'Oracle , le besoin de variable volatile se pose pour résoudre 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 dans une variable volatile établit une relation passe-avant avec les lectures suivantes de cette même variable.

Cela signifie que les modifications apportées à une volatilevariable sont toujours visibles pour les autres threads. Cela signifie également que lorsqu'un thread lit une variable volatile, il voit non seulement la dernière modification apportée à la volatile, mais également les effets secondaires du code qui a provoqué la modification.

Comme expliqué en Peter Parkerréponse, en l'absence de volatilemodificateur, la pile de chaque thread peut avoir sa propre copie de variable. En rendant la variable as volatile, les problèmes de cohérence de la mémoire ont été corrigés.

Jetez un œil à la page du didacticiel jenkov pour une meilleure compréhension.

Jetez un coup d'œil à la question SE connexe pour plus de détails sur les cas volatils et les cas d'utilisation de volatils:

Différence entre volatile et synchronisé en Java

Un cas d'utilisation pratique:

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

Ravindra babu
la source
1

Les variables volatiles sont une synchronisation légère. Lorsque la visibilité des données les plus récentes sur tous les threads est requise et que l'atomicité peut être compromise, dans de telles situations, les variables volatiles doivent être préférées. La lecture des variables volatiles renvoie toujours l'écriture la plus récente effectuée par n'importe quel thread car elles ne sont ni mises en cache dans les registres ni dans les caches où les autres processeurs ne peuvent pas voir. Volatile est sans verrou. J'utilise volatile, lorsque le scénario répond aux critères mentionnés ci-dessus.

Neha Vari
la source
-1

La clé volatile, lorsqu'elle est utilisée avec une variable, s'assurera que les threads lisant cette variable verront la même valeur. Maintenant, si vous avez plusieurs threads lisant et écrivant dans une variable, rendre la variable volatile ne sera pas suffisant et les données seront corrompues. Les threads d'image ont lu la même valeur mais chacun a effectué quelques changements (par exemple incrémenté un compteur), lors de la réécriture dans la mémoire, l'intégrité des données est violée. C'est pourquoi il est nécessaire de synchroniser les variables (différentes manières sont possibles)

Si les changements sont effectués par 1 thread et que les autres ont juste besoin de lire cette valeur, le volatile conviendra.

Java Main
la source
-1

La variable volatile est essentiellement utilisée pour la mise à jour instantanée (vidage) dans la ligne de cache partagée principale une fois mise à jour, de sorte que les modifications se reflètent immédiatement sur tous les threads de travail.

Niyaz Ahamad
la source
-2

Ci-dessous est un code très simple pour démontrer l'exigence de la volatilevariable for qui est utilisée pour contrôler l'exécution du thread à partir d'un autre thread (c'est un scénario où cela volatileest requis).

// Code to prove importance of 'volatile' when state of one thread is being mutated from another thread.
// Try running this class with and without 'volatile' for 'state' property of Task class.
public class VolatileTest {
    public static void main(String[] a) throws Exception {
        Task task = new Task();
        new Thread(task).start();

        Thread.sleep(500);
        long stoppedOn = System.nanoTime();

        task.stop(); // -----> do this to stop the thread

        System.out.println("Stopping on: " + stoppedOn);
    }
}

class Task implements Runnable {
    // Try running with and without 'volatile' here
    private volatile boolean state = true;
    private int i = 0;

    public void stop() {
        state = false;
    } 

    @Override
    public void run() {
        while(state) {
            i++;
        }
        System.out.println(i + "> Stopped on: " + System.nanoTime());
    }
}

Lorsque volatilen'est pas utilisé: vous ne verrez jamais le message « Arrêté sur: xxx » même après « Arrêt sur: xxx » et le programme continue de s'exécuter.

Stopping on: 1895303906650500

Lorsqu'il est volatileutilisé: vous verrez immédiatement le message « Arrêté: xxx ».

Stopping on: 1895285647980000
324565439> Stopped on: 1895285648087300

Démo: https://repl.it/repls/SilverAgonizingObjectcode

manikanta
la source
Vers le bas: votez pour expliquer pourquoi vers le bas? Si ce n'est pas vrai, au moins j'apprendrai ce qui ne va pas. J'ai ajouté ce même commentaire deux fois, mais je ne sais pas qui supprime encore et encore
manikanta