L'utilisation de la synchronisation en java, le principe de la synchronisation et la façon dont la synchronisation fournit l'atomicité, la visibilité et la garantie de commande

Afin de résoudre les problèmes d'atomicité, de visibilité et d'ordre dans la programmation concurrente, le langage Java fournit une série de mots-clés liés au traitement concurrent, tels que , , , synchronizedetc.volatilefinalconcurren包

Dans "Compréhension approfondie de la machine virtuelle Java", il y a un tel passage :

synchronizedLes mots clés peuvent être utilisés comme l'une des solutions lorsque les trois caractéristiques d'atomicité, de visibilité et d'ordre sont requises, et cela semble être "universel". En effet, la plupart des opérations de contrôle de concurrence peuvent être effectuées à l'aide de synchronized.

Ensuite, cet article tourne autour synchronizedde cela, introduisant principalement synchronizedl'utilisation, synchronizedles principes et synchronizedla manière de fournir des garanties d'atomicité, de visibilité et de commande.

utilisation de la synchronisation

synchronizedEst un mot-clé de contrôle de concurrence fourni par Java. Il existe deux utilisations principales, qui sont les méthodes synchronisées et les blocs de code synchronisés. Autrement dit, synchronizedles méthodes et les blocs de code peuvent être décorés.

/**
 * @author Hollis 18/08/04.
 */
public class SynchronizedDemo {
     //同步方法
    public synchronized void doSth(){
        System.out.println("Hello World");
    }

    //同步代码块
    public void doSth1(){
        synchronized (SynchronizedDemo.class){
            System.out.println("Hello World");
        }
    }
}

Les blocs de code et les méthodes modifiés synchronizedne sont accessibles que par un seul thread à la fois.

Le principe de réalisation de synchronisation

synchronized, est un mot-clé très important en Java pour résoudre l'accès à la synchronisation des données dans des conditions concurrentes. synchronizedLorsque nous voulons nous assurer qu'une ressource partagée n'est accessible que par un thread à la fois, nous pouvons utiliser des mots-clés pour verrouiller des classes ou des objets dans le code .

Dans [Compréhension approfondie du multi-threading (1) - Principe d'implémentation synchronisée] [2], j'ai présenté son principe d'implémentation. Afin d'assurer l'intégrité des connaissances, voici une brève introduction. Pour plus de détails, veuillez lire le texte original.

Nous décompilons le code ci-dessus pour obtenir le code suivant :

public synchronized void doSth();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String Hello World
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return

  public void doSth1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: ldc           #5                  // class com/hollis/SynchronizedTest
         2: dup
         3: astore_1
         4: monitorenter
         5: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         8: ldc           #3                  // String Hello World
        10: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        13: aload_1
        14: monitorexit
        15: goto          23
        18: astore_2
        19: aload_1
        20: monitorexit
        21: aload_2
        22: athrow
        23: return

Il ressort du code décompilé que : pour les méthodes de synchronisation, JVM utilise ACC_SYNCHRONIZEDdes marqueurs pour réaliser la synchronisation. Pour les blocs de code synchrones. La JVM utilise monitorenterdeux monitorexitinstructions pour réaliser la synchronisation.

Dans [The Java® Virtual Machine Specification][3], il y a une introduction aux principes d'implémentation des méthodes de synchronisation et des blocs de code de synchronisation. Je la traduis en chinois comme suit :

La synchronisation au niveau de la méthode est implicite. Il y aura un indicateur dans le pool de constantes de la méthode synchronized ACC_SYNCHRONIZED. Lorsqu'un thread veut accéder à une méthode, il vérifie si elle est disponible ACC_SYNCHRONIZED. S'il est défini, il doit d'abord obtenir le verrou du moniteur, puis commencer à exécuter la méthode et relâcher le verrou du moniteur après l'exécution de la méthode. À ce stade, si d'autres threads demandent à exécuter la méthode, ils seront bloqués car ils ne peuvent pas obtenir le verrou du moniteur. Il convient de noter que si une exception se produit pendant l'exécution de la méthode et que l'exception n'est pas gérée à l'intérieur de la méthode, le verrou du moniteur sera automatiquement libéré avant que l'exception ne soit levée en dehors de la méthode.

Les blocs de code synchronisés sont implémentés à l'aide de deux directives monitorenteret . monitorexitL'exécution monitorenterd'instructions peut être comprise comme un verrouillage, et l'exécution monitorexitpeut être comprise comme une libération de verrous. Chaque objet maintient un compteur qui enregistre le nombre de fois qu'il a été verrouillé. Le compteur de l'objet déverrouillé est 0. Lorsqu'un thread acquiert le verrou (exécution monitorenter), le compteur incrémente à 1. Lorsque le même thread acquiert à nouveau le verrou de l'objet, le compteur incrémente à nouveau. Lorsque le même thread libère le verrou (exécute monitorexitl'instruction), le compteur est à nouveau décrémenté. quand le compteur est à 0. Le verrou sera libéré et d'autres threads pourront acquérir le verrou.

Peu importe qu'il soit ACC_SYNCHRONIZEDou monitorenter, monitorexitil est implémenté sur la base de Monitor. Dans la machine virtuelle Java (HotSpot), Monitor est implémenté sur la base de C++ et implémenté par ObjectMonitor.

Plusieurs méthodes sont fournies dans la classe ObjectMonitor, telles que enter, exit, wait, notify, notifyAlletc. sychronizedLors du verrouillage, la méthode enter de objectMonitor sera appelée et la méthode exit sera appelée lors du déverrouillage. (Pour plus de détails sur Monitor, voir [Compréhension approfondie du multithreading (4) - le principe de réalisation de Monitor][4])

synchronisé et atomicité

L'atomicité signifie qu'une opération est ininterruptible et qu'elle doit être exécutée complètement, sinon elle ne sera pas exécutée du tout.

Que se passe-t-il avec notre problème de multithreading dans [la programmation concurrente de Java ? ] [5] analysé : le thread est l'unité de base de l'ordonnancement du CPU. Le processeur a le concept de tranches de temps et effectuera la planification des threads selon différents algorithmes de planification. Lorsqu'un thread commence à s'exécuter après avoir obtenu la tranche de temps, une fois la tranche de temps épuisée, il perd le droit d'utiliser le processeur. Par conséquent, dans un scénario multithread, étant donné que les tranches de temps sont alternées entre les threads, des problèmes d'atomicité se produiront.

En Java, afin d'assurer l'atomicité, deux instructions de bytecode de haut niveau monitorenteret sont fournies monitorexit. Comme mentionné précédemment, ces deux instructions de bytecode sont les mots-clés correspondants en Java synchronized.

Grâce monitorenterà l'instruction et monitorexit, il peut être garanti que synchronizedle code modifié n'est accessible que par un thread à la fois et ne peut pas être consulté par d'autres threads jusqu'à ce que le verrou soit libéré. Par conséquent, il peut être utilisé en Java synchronizedpour garantir que les opérations dans les méthodes et les blocs de code sont atomiques.

Lorsque le thread 1 exécute monitorenterl'instruction, il verrouille le moniteur. Après le verrouillage, les autres threads ne peuvent pas obtenir le verrou à moins que le thread 1 ne le déverrouille activement. Même pendant l'exécution, pour une raison quelconque, telle que la tranche de temps CPU est épuisée, le thread 1 abandonne le CPU, mais il ne le déverrouille pas. Et parce que synchronizedle verrou est réentrant, la prochaine tranche de temps ne peut être acquise que par lui-même, et le code continuera à être exécuté. jusqu'à ce que tous les codes soient exécutés. Cela garantit l'atomicité.

synchronisé et visibilité

La visibilité signifie que lorsque plusieurs threads accèdent à la même variable, un thread modifie la valeur de la variable et les autres threads peuvent voir immédiatement la valeur modifiée.

Nous sommes dans [Si quelqu'un vous demande quel est le modèle de mémoire Java, envoyez-lui cet article. ][1] analysé : Le modèle de mémoire Java stipule que toutes les variables sont stockées dans la mémoire principale, et chaque thread a sa propre mémoire de travail, et la mémoire de travail du thread stocke la mémoire principale des variables utilisées dans le thread. copier copier, toutes les opérations sur les variables par les threads doivent être effectuées dans la mémoire de travail et ne peuvent pas lire et écrire directement dans la mémoire principale. Différents threads ne peuvent pas accéder directement aux variables dans la mémoire de travail de l'autre, et le transfert de variables entre les threads nécessite une synchronisation des données entre leur propre mémoire de travail et la mémoire principale. Par conséquent, il peut arriver que le thread 1 modifie la valeur d'une variable, mais que le thread 2 ne soit pas visible.

Comme nous l'avons mentionné précédemment, synchronizedle code modifié sera verrouillé lorsqu'il commencera à s'exécuter et sera déverrouillé une fois l'exécution terminée. Afin d'assurer la visibilité, il existe une règle comme celle-ci : avant de déverrouiller une variable, la variable doit être resynchronisée avec la mémoire principale. Après le déverrouillage de cette manière, les threads suivants peuvent accéder à la valeur modifiée.

Par conséquent, la valeur de l'objet verrouillé par le mot clé synchronized est visible.

synchronisé et ordonné

La séquence signifie que l'ordre d'exécution du programme est exécuté dans l'ordre du code.

Nous sommes dans [Si quelqu'un vous demande quel est le modèle de mémoire Java, envoyez-lui cet article. ][1] analysé : en plus de l'introduction de tranches de temps, en raison de l'optimisation du processeur et du réarrangement des instructions, le CPU peut également exécuter le code d'entrée dans le désordre, tel que load->add->save peut être optimisé pour charger - > enregistrer-> ajouter. C'est là qu'il peut y avoir un problème d'ordre.

Il convient de noter ici que synchronizedle réarrangement des instructions et l'optimisation du processeur ne peuvent pas être interdits. C'est-à-dire que synchronizedles problèmes mentionnés ci-dessus ne peuvent pas être évités.

Alors, pourquoi dites-vous que synchronizeddes garanties de commande sont également fournies ?

Il s'agit d'élargir le concept d'ordre. L'ordre naturel dans les programmes Java peut se résumer en une phrase : Si observé dans ce fil, toutes les opérations sont naturellement ordonnées. Si un thread en observe un autre, toutes les opérations sont désordonnées.

La phrase ci-dessus est également la phrase originale de "Compréhension approfondie de la machine virtuelle Java", mais comment la comprendre ? Zhou Zhiming n'a pas expliqué en détail. Ici, je vais brièvement développer, ce qui est en fait as-if-serial语义lié à .

as-if-serialLa sémantique signifie : quelle que soit la réorganisation (compilateur et processeur pour améliorer le parallélisme), le résultat d'exécution d'un programme monothread ne peut pas être modifié. Peu importe comment les compilateurs et les processeurs optimisent, as-if-serialla sémantique doit être respectée.

as-if-serial语义Je ne rentrerai pas dans les détails ici as-if-serial语义, en termes simples, il est garanti que dans un même thread, il y a certaines restrictions sur le réarrangement des instructions, et tant que le compilateur et le processeur respectent cette sémantique, alors on peut considérer que le seul le programme de thread est exécuté dans l'ordre. Bien sûr, il y a en fait des réarrangements, mais nous n'avons pas à nous soucier de l'interférence de tels réarrangements.

Ainsi, en raison synchronizeddu code modifié, il n'est accessible que par le même thread en même temps. Il s'agit alors d'une exécution monothread. Par conséquent, sa commande peut être garantie.

synchronisation et optimisation des serrures

synchronizedL'utilisation, le principe et l'effet sur la programmation concurrente présentés précédemment . est un excellent mot-clé à utiliser.

synchronizedEn fait, il est implémenté à l'aide de Monitor. La méthode de objectMonitor sera appelée lors du verrouillage enter, et la méthode sera appelée lors du déverrouillage exit. En fait, seulement avant JDK1.6, l'implémentation de synchronized appellera directement la entersomme d'ObjectMonitor exitCe type de verrou est appelé un verrou lourd.

Par conséquent, dans JDK1.6, de nombreuses optimisations ont été apportées aux verrous, puis il y a des verrous légers, des verrous biaisés, l'élimination des verrous, des verrous de rotation adaptatifs et le grossissement des verrous (les verrous de rotation existent dans la version 1.4, mais la valeur par défaut est désactivée , jdk1.6 est activé par défaut), ces opérations consistent à partager plus efficacement les données entre les threads et à résoudre les problèmes de concurrence.

Pour les verrous tournants, le grossissement des verrous et l'élimination des verrous, veuillez vous référer à [Compréhension approfondie du multithreading (5) - technologie d'optimisation des verrous pour les machines virtuelles Java] [6]. Concernant les verrous légers et les verrous biaisés, ils sont déjà dans la planification de la planification, Je présenterai un article séparé plus tard, et le publierai exclusivement sur mon blog (http://www.hollishuang.com) et mon compte officiel (Hollis), alors restez à l'écoute.

Eh bien, concernant synchronizedles mots-clés, nous avons présenté leur utilisation, leurs principes et comment assurer l'atomicité, la séquence et la visibilité. En même temps, nous avons également étendu les informations et la réflexion liées à l'optimisation des serrures.

Je suppose que tu aimes

Origine blog.csdn.net/zy_dreamer/article/details/132350503
conseillé
Classement