De nombreux amis ont peut-être entendu parler du mot-clé volatile et peuvent l'avoir utilisé. Avant Java 5, c'était un mot-clé controversé, car l'utiliser dans des programmes a souvent abouti à des résultats inattendus. Ce n'est qu'après que Java 5 a fait le mot clé volatil qui a retrouvé sa vitalité. Bien que le mot-clé volatil soit littéralement simple à comprendre, il n'est pas facile de bien l'utiliser.
1. Préface
JMM fournit une définition de variable volatile, des blocs finaux et synchronisés pour assurer la visibilité.
Pour les variables modifiées avec volatile, le thread lira la valeur la plus modifiée de la variable chaque fois qu'il utilise la variable. Volatile est facilement utilisé à mauvais escient et utilisé pour les opérations atomiques. J'ai écrit quelques exemples de test, vous pouvez essayer.
2. Programme principal
classe publique main {public static void main (String [] args) lève InterruptedException {list <read> threadList = new ArrayList <read> (); for (int i = 0; i <10; ++ i) {thread thread = new Thread (new Runnable () {@OverRidePublic Void run () {Single.holder.instance.add ();}}); threadList.add (thread); thread.start ();} pour (thread thread: ThreadList) thread.join (); System.out.println (single.holder.instance.x);}}3. Test de mode singleton
1. Pas de volatile, pas de synchronisé
classe Single {public int x = 0; public void add () {try {timeunit.milliseconds.sleep (50);} catch (InterruptedException e) {e.printStackTrace ();} ++ this.x;} public static static static {public static single instance = new single ();}}Résultats de sortie: 8, 9 et 10 sont tous apparus. Vous pouvez exécuter plus et essayer plus et vous trouverez différents résultats.
2. Il y a volatile, mais pas synchronisé
classe Single {public volatile int x = 0; public void add () {try {timeunit.milliseconds.sleep (50);} catch (interruptedException e) {e.printStackTrace ();} ++ this.x;} public static class static {public static single instance = new single ();}}Résultat de sortie: le nombre maximum d'occurrences est de 9 et 10.
3. Pas de volatile, synchronisé
classe Single {public int x = 0; public synchronisé void add () {try {timeunit.milliseconds.sleep (50);} catch (interruptedException e) {e.printstackTrace ();} ++ this.x;} public class static {public static single instance = new single ();}}Résultat de sortie: Peu importe le nombre de fois que vous exécutez, ce sera 10.
4. À propos de l'application de volatile dans DCL (Double Check Lock)
classe publique Lazysingleton {private int somefield; instance privée de lazysingleton statique; private lazysingleton () {this.somefield = new random (). NextInt (200) +1; // (1)} public static Lazysingleton getInstance () {if (instance == null) {// (2) synchronisé (lazysingleton.class) {// (3) if (instance == null) {// (4) instance = new LazySingleton (); // (5)}}} Instance de retour; // (6)} public int getomefield () {return this.SomeField; // (7)}}Tout d'abord, permettez-moi d'expliquer pourquoi cette méthode d'écriture ne fonctionne pas en Java!
Supposons que le thread I appelle la méthode getInstance () pour la première fois, puis Thread II appelle également la méthode getInstance () et la méthode getomefield (). Ce que nous voulons expliquer, c'est que l'énoncé du thread I (1) n'est pas un énoncé auparavant du thread II (7). Lorsque Thread II exécute l'instruction (2) de la méthode getInstance (), car l'accès à l'instance n'est pas dans le bloc synchrone, Thread II peut ou non observer l'écriture de Thread I sur instance dans l'instruction (5), c'est-à-dire que la valeur de l'instance peut être vide ou non vide. Nous supposons d'abord que la valeur de l'instance n'est pas vide, nous observons donc que le fil j'écrit l'instance. À l'heure actuelle, Thread II exécutera l'instruction (6) et renvoie directement la valeur de cette instance, puis appellera la méthode getomefield () sur cette instance. Cette méthode est également appelée sans aucune synchronisation. Par conséquent, l'ensemble du fonctionnement du thread II est appelé sans synchronisation. Cela montre qu'il n'y a pas de relation inoffensive entre l'énoncé (1) du thread I et l'énoncé (7) du thread II. Cela signifie que Thread II peut ne pas être en mesure d'observer la valeur écrite par le fil I à certains filés à l'énoncé (1). C'est le problème avec DCL. C'est ridicule, non? DCL était à l'origine destiné à échapper à la synchronisation et il a atteint cet objectif. C'est précisément à cause de cela qu'il a finalement été puni. Il y a de sérieux bugs dans de tels programmes, bien que la probabilité de découverte d'un tel bug soit bien inférieure à la probabilité de gagner à la loterie, et elle est éphémère. Ce qui est plus terrifiant, c'est que même si cela se produit, vous ne penserez pas qu'il a été causé par DCL.
Ma compréhension est que le fil I et le fil II ont leur propre stockage de travail. Après le thread, je crée l'instance, le temps pour actualiser la mémoire est incertain, il est donc tout à fait possible que le thread II ne puisse pas observer la valeur écrite par le fil I à certains filés à l'instruction (1).
Donc, car il y a une règle supplémentaire avant-celle ajoutée dans Java 5:
• Écrivez l'opération dans le champ volatil qui se passe avant les opérations de lecture ultérieures du même champ.
En utilisant cette règle, nous pouvons déclarer l'instance comme volatile, c'est-à-dire: instance privée de Lazysingleton statique volatile;
According to this rule, we can obtain the statement of thread I (5) -> sentence of thread II (2) (that is, thread), according to the single-thread rule, the statement of thread I (1) -> sentence of thread I (5) and sentence of thread II (2) -> sentence of thread II (7), and according to the delivery rules, there are the statement of thread I (1) -> sentence of thread II (7), which means that thread II can observe the write value of thread I to Certains déposés dans la déclaration (1), et le programme peut obtenir le comportement correct.
Supplément: Avant Java5, il n'y a pas de différence entre la sémantique synchrone du champ final et d'autres variables. Dans Java5, une fois que la variable finale est définie dans le constructeur (à condition que cette référence ne soit pas divulguée dans le constructeur), d'autres threads verront certainement les valeurs définies dans le constructeur. Le problème avec DCL est juste que nous voyons la valeur par défaut de la variable membre de l'objet, afin que nous puissions définir la variable SomeField de Lazysingleton sur Final, afin qu'il puisse fonctionner correctement dans Java5.
Le contenu ci-dessus est la connaissance des mots clés volatils dans Java qui vous sont présentés par l'éditeur. J'espère que ce sera utile à tous!