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

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

Théorie et pratique Java: gérer la volatilité

Étant donné les détails dans lesquels 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é correctement?

    volatile a sémantique pour la visibilité de la mémoire. Fondamentalement, la valeur d’un champ volatile devient visible pour tous les lecteurs (autres threads en particulier) après qu’une opération d’écriture se termine sur elle. Sans volatile , les lecteurs pourraient voir une 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 c’est true . La condition peut être définie sur false en appelant une méthode “stop”. La boucle voit false et se termine lorsqu’elle teste la valeur après la fin de l’exécution de la méthode stop.

    Le livre ” Concurrence Java en pratique “, que je recommande vivement, donne une bonne explication de la 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 article de volatile est ce que son article appelle le “drapeau de statut 1”.

    Si vous voulez en savoir plus sur le fonctionnement volatile sous le capot, lisez le modèle de mémoire Java . Si vous souhaitez aller au-delà de ce niveau, consultez un bon livre sur l’architecture informatique, comme Hennessy & Patterson, et découvrez la cohérence du cache et la cohérence du cache.

    “… 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.concurrent qui traite du comportement atomique.

    Le post Wikipedia sur un Singleton Pattern montre une utilisation volatile.

    Point important sur les volatile :

    1. La synchronisation en Java est possible en utilisant des mots-clés Java synchronized et volatile et des verrous.
    2. En Java, nous ne pouvons pas avoir de variable synchronized . L’utilisation d’ synchronized 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 java, qui demandera 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, il n’est pas nécessaire d’utiliser le mot clé volatile .

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

    Si nous ne rendons pas la variable _instance volatile le thread qui crée l’instance de Singleton ne peut pas communiquer avec l’autre thread. 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 null null et ils croiront qu’il est toujours assigné null.

    Pourquoi cela arrive-t-il? Comme les threads de lecture ne font aucun locking et que le thread d’ _instance ne _instance pas 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 ces mises à jour seront visibles par tous les threads de lecteur.

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

    Exemple d’utilisation de sans volatilité:

     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 compatible avec les threads. 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 fini son exécution. Cela signifie que la méthode getInstance () renvoie un object 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’object a terminé son exécution.
    La source

    entrer la description de l'image ici

    utilisation volatile en Java :

    Les iterators rapides sont généralement implémentés à l’aide d’un compteur volatile sur l’object liste.

    • Lorsque la liste est mise à jour, le compteur est incrémenté.
    • Lorsqu’un Iterator est créé, la valeur actuelle du compteur est intégrée à l’object Iterator .
    • Lorsqu’une opération Iterator est effectuée, la méthode compare les deux valeurs de compteur et génère une ConcurrentModificationException si elles sont différentes.

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

    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 bons pools de threads. Mais si vous êtes certain d’avoir besoin d’un thread, 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 } } 

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

    Un exemple courant d’utilisation de volatile est d’utiliser une variable volatile boolean 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 thread différent, vous pouvez demander au thread de vérifier régulièrement un indicateur. Pour l’arrêter, définissez l’indicateur sur true. En rendant 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érifie sans même avoir à utiliser un bloc synchronized .

    Oui, volatile doit être utilisé chaque fois que vous souhaitez accéder à une variable mutable par plusieurs threads. Ce n’est pas très commun, car vous devez généralement effectuer plus d’une opération atomique (par exemple, vérifier l’état de la variable avant de le modifier), auquel cas vous utiliseriez un bloc synchronisé.

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

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

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

    Une variable déclarée avec un 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 (microprocesseur) de l’ordinateur par aucun thread. L’access a toujours eu lieu depuis la mémoire principale.

    2. S’il y a une opération d’écriture 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

    • Tous les threads lisant une variable volatile liront définitivement la dernière valeur. Parce qu’aucune valeur mise en cache ne peut le polluer. Et aussi la demande de lecture ne sera accordée qu’après l’achèvement de l’opération d’écriture en cours.

    Et d’autre part,

    • Si nous examinons plus en détail le n ° 2 que j’ai mentionné, nous pouvons voir que le mot-clé volatile est un moyen idéal pour maintenir une variable partagée qui a un nombre de threads de lecture et un seul thread d’écriture pour y accéder. Une fois que nous avons ajouté le mot-clé volatile , c’est fait. Pas de surcharge pour la sécurité des fils.

    Inversement,

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

    Vous devrez utiliser des mots clés «volatiles» ou «synchronisés» et tout autre outil ou technique de contrôle de la concurrence que vous pourriez avoir à votre disposition si vous développez une application multithread. Les applications de bureau sont un exemple d’une telle application.

    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 vous-même 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 faites le contrôle de concurrence que dans une telle application si vous implémentez des objects singleton. Cela a déjà été résolu si vous sortingcotez 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 qui utilise un framework IoC comme Spring ou EJB, vous n’avez pas besoin d’utiliser «volatile».

    volatile ne 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. 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 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(Ssortingng args[]) throws InterruptedException{ List list = new ArrayList(); 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, ils 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(Ssortingng args[]) throws InterruptedException{ List list = new ArrayList(); 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); } } } 

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

    1. Vous ne devriez utiliser volatile que si vous comprenez complètement ce qu’il fait et comment il diffère de la synchronisation. Dans de nombreuses situations, volatil apparaît, à première vue, comme une alternative plus simple et plus performante à la synchronisation, alors que, souvent, une meilleure compréhension des volatiles indiquerait clairement que la synchronisation est la seule option qui fonctionnerait.
    2. volatile ne fonctionne pas vraiment dans beaucoup d’anciennes machines virtuelles, bien que synchronisé. Je me souviens d’avoir vu un document faisant référence aux différents niveaux de support dans différentes JVM mais malheureusement je ne le trouve pas maintenant. Si vous utilisez Java pre 1.5 ou si vous n’avez pas le contrôle sur les JVM sur lesquelles votre programme sera exécuté, examinez-le définitivement.

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

    Chaque thread accédant à un champ volatil 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.

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

    1. Empêche JVM de lire les valeurs de register (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 JVM de lire les valeurs dans le registre et force sa valeur à être extraite de la mémoire.

    Un indicateur d’occupation est utilisé pour empêcher la poursuite d’un thread tant 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ésactive l’ indicateur occupé :

     busy = 0; 

    Cependant, étant donné qu’on accède fréquemment à busy dans le thread de test, la JVM peut optimiser le test en plaçant la valeur busy dans un registre, puis tester le contenu du registre sans lire la valeur busy dans la mémoire avant chaque test. Le thread de test ne verrait jamais de changement occupé et l’autre thread ne modifierait que la valeur occupée en mémoire, ce qui entraînerait un blocage. La déclaration du drapeau 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 sur une variable volatile établit une relation “se produit 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.

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

    Une action atomique est une action qui se produit tout à la fois. Une action atomique ne peut pas s’arrêter au milieu: elle se produit complètement ou 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 que vous pouvez spécifier qui sont atomiques:

    • Les lectures et les é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é!

    Les variables volatiles sont une synchronisation légère. Lorsque la visibilité des dernières données entre tous les threads est requirejse et que l’atomicité peut être compromise, dans de telles situations, les variables volatiles doivent être préférées. Les variables volatiles en lecture renvoient toujours les écritures les plus récentes effectuées par un 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 locking. J’utilise volatile, quand le scénario répond aux critères mentionnés ci-dessus.

    À partir de 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 sur une variable volatile établit une relation avant / après 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 volatile , mais également les effets secondaires du code qui a conduit à la modification.

    Comme expliqué dans la réponse de Peter Parker , en l’absence de modificateur volatile , la stack de chaque thread peut avoir sa propre copie de variable. En rendant la variable aussi volatile , les problèmes de cohérence de la mémoire ont été corrigés.

    Jetez un oeil à la page du tutoriel jenkov pour une meilleure compréhension.

    Jetez un coup d’œil à la question SE associée pour plus de détails sur les cas volatils et les cas d’utilisation pour utiliser les volatiles:

    Différence entre volatile et synchronisé en Java

    Un cas pratique d’utilisation:

    Vous avez plusieurs threads qui doivent imprimer l’heure courante dans un format particulier, par exemple: java.text.SimpleDateFormat("HH-mm-ss") . Yon peut avoir une classe, qui convertit l’heure courante en SimpleDateFormat et 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.

    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 à lire et à écrire dans une variable, rendre la variable volatile ne sera pas suffisante et les données seront corrompues. Les threads d’image ont lu la même valeur mais chacun a effectué des changements (par exemple, un compteur incrémenté). Lors de l’écriture en mémoire, l’intégrité des données est violée. C’est pourquoi il est nécessaire de synchroniser la variable (des manières différentes sont possibles)

    Si les modifications sont effectuées par 1 thread et que les autres doivent simplement lire cette valeur, le volatile sera approprié.

    Volatile fait suite.

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

    Le code suivant montre comment un certain nombre de threads peuvent s’exécuter dans un ordre prédéfini et des sorties d’impression 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 courant à part entière suivant.

     public class Solution { static volatile int counter = 0; static int print = 0; public static void main(Ssortingng[] 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

    J’aime l’explication de jenkov

    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 du processeur, et que chaque écriture sur une variable volatile sera écrite dans la mémoire principale. .

    En fait, depuis Java 5, le mot-clé volatile garantit plus que simplement que les variables volatiles sont écrites et lues dans la mémoire principale. Il s’agit d’une garantie de visibilité étendue appelée garantie-avant-passe.

    Considérations de performance de volatiles

    La lecture et l’écriture de variables volatiles permettent de lire ou d’écrire la variable dans la mémoire principale. La lecture et l’écriture dans la mémoire principale sont plus coûteuses que l’access au cache du processeur. L’access aux variables volatiles empêche également le réordonnancement des instructions, ce qui constitue une technique d’amélioration des performances normale. Ainsi, vous ne devriez utiliser que des variables volatiles lorsque vous avez vraiment besoin d’imposer la visibilité des variables.

    Une variable Volatile est modifiée de manière asynchrone en exécutant simultanément des threads dans une application Java. Il n’est pas permis d’avoir une copie locale d’une variable différente de la valeur actuellement conservée dans la mémoire “principale”. En effet, une variable déclarée volatile doit avoir ses données synchronisées sur 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 access plus élevé et une surcharge de mise à jour que les variables «simples», car la raison pour laquelle les threads peuvent avoir leur propre copie de données est pour une meilleure efficacité.

    Lorsqu’un champ est déclaré volatile, le compilateur et le runtime sont avertis que cette variable est partagée et que les opérations ne doivent pas être réorganisé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. processeurs, donc une lecture d’une variable volatile renvoie toujours la plus récente écriture par n’importe quel thread.

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