web-dev-qa-db-fra.com

Que signifie "synchronisé"?

J'ai quelques questions concernant l'utilisation et la signification du mot clé synchronized.

  • Quelle est la signification du mot clé synchronized?
  • Quand les méthodes devraient-elles être synchronized?
  • Qu'est-ce que cela signifie par programme et par logique?
932
Johanna

Le mot clé synchronized concerne tous les threads qui lisent et écrivent dans les mêmes variables, objets et ressources. Ce n'est pas un sujet trivial en Java, mais voici une citation de Sun:

Les méthodes synchronized permettent une stratégie simple pour éviter les interférences de thread et les erreurs de cohérence de la mémoire: si un objet est visible par plusieurs threads, toutes les lectures ou écritures sur les variables de cet objet sont effectuées à l'aide de méthodes synchronisées.

En très très petit mot: Lorsque vous avez deux threads qui lisent et écrivent dans la même 'ressource', dites une variable nommée foo, vous doivent s’assurer que ces threads ont accès à la variable de manière atomique. Sans le mot clé synchronized, votre thread 1 peut ne pas voir le thread de modification 2 modifié en foo, ou pire, il ne peut être modifié que de moitié. Ce ne serait pas ce à quoi vous vous attendez logiquement.

Là encore, il s'agit d'un sujet non trivial en Java. Pour en savoir plus, explorez les sujets ici sur SO et sur les Interwebs sur:

Continuez à explorer ces sujets jusqu'à ce que le nom "Brian Goetz" soit associé de manière permanente au terme "concurrency" dans votre cerveau.

839
Stu Thompson

Eh bien, je pense que nous en avions assez des explications théoriques, alors considérons ce code

public class SOP {
    public static void print(String s) {
        System.out.println(s+"\n");
    }
}

public class TestThread extends Thread {
    String name;
    TheDemo theDemo;
    public TestThread(String name,TheDemo theDemo) {
        this.theDemo = theDemo;
        this.name = name;
        start();
    }
    @Override
    public void run() {
        theDemo.test(name);
    }
}

public class TheDemo {
    public synchronized void test(String name) {
        for(int i=0;i<10;i++) {
            SOP.print(name + " :: "+i);
            try{
                Thread.sleep(500);
            } catch (Exception e) {
                SOP.print(e.getMessage());
            }
        }
    }
    public static void main(String[] args) {
        TheDemo theDemo = new TheDemo();
        new TestThread("THREAD 1",theDemo);
        new TestThread("THREAD 2",theDemo);
        new TestThread("THREAD 3",theDemo);
    }
}

Remarque: synchronized bloque l'appel du prochain thread à la méthode test () tant que l'exécution du précédent thread n'est pas terminée. Les threads peuvent accéder à cette méthode une à la fois. Sans synchronized, tous les threads peuvent accéder à cette méthode simultanément.

Lorsqu'un thread appelle la méthode synchronisée 'test' de l'objet (ici, l'objet est une instance de la classe 'TheDemo'), il acquiert le verrou de cet objet. Tout nouveau thread ne peut appeler AUCUNE méthode synchronisée du même objet tant que le thread précédent qui avait acquis la serrure ne lâche pas la serrure.

La même chose se produit quand une méthode synchronisée statique de la classe est appelée. Le thread acquiert le verrou associé à la classe (dans ce cas, toute méthode synchronisée non statique d'une instance de cette classe peut être appelée par n'importe quel thread car ce verrou au niveau de l'objet est toujours disponible). Tout autre thread ne pourra appeler aucune méthode synchronisée de la classe tant que le verrou de niveau de classe n'est pas libéré par le thread qui détient actuellement le verrou.

Sortie avec synchronisé

THREAD 1 :: 0
THREAD 1 :: 1
THREAD 1 :: 2
THREAD 1 :: 3
THREAD 1 :: 4
THREAD 1 :: 5
THREAD 1 :: 6
THREAD 1 :: 7
THREAD 1 :: 8
THREAD 1 :: 9
THREAD 3 :: 0
THREAD 3 :: 1
THREAD 3 :: 2
THREAD 3 :: 3
THREAD 3 :: 4
THREAD 3 :: 5
THREAD 3 :: 6
THREAD 3 :: 7
THREAD 3 :: 8
THREAD 3 :: 9
THREAD 2 :: 0
THREAD 2 :: 1
THREAD 2 :: 2
THREAD 2 :: 3
THREAD 2 :: 4
THREAD 2 :: 5
THREAD 2 :: 6
THREAD 2 :: 7
THREAD 2 :: 8
THREAD 2 :: 9

Sortie sans synchronisé

THREAD 1 :: 0
THREAD 2 :: 0
THREAD 3 :: 0
THREAD 1 :: 1
THREAD 2 :: 1
THREAD 3 :: 1
THREAD 1 :: 2
THREAD 2 :: 2
THREAD 3 :: 2
THREAD 1 :: 3
THREAD 2 :: 3
THREAD 3 :: 3
THREAD 1 :: 4
THREAD 2 :: 4
THREAD 3 :: 4
THREAD 1 :: 5
THREAD 2 :: 5
THREAD 3 :: 5
THREAD 1 :: 6
THREAD 2 :: 6
THREAD 3 :: 6
THREAD 1 :: 7
THREAD 2 :: 7
THREAD 3 :: 7
THREAD 1 :: 8
THREAD 2 :: 8
THREAD 3 :: 8
THREAD 1 :: 9
THREAD 2 :: 9
THREAD 3 :: 9
280
Dheeraj Sachan

Le mot clé synchronized empêche l'accès simultané à un bloc de code ou à un objet par plusieurs threads. Par défaut, un Hashtable est synchronized, de sorte qu'un seul thread peut accéder à la table à la fois.

Lors de l'utilisation de constructions non-synchronized comme HashMap, vous devez créer des fonctionnalités de sécurité des threads dans votre code pour éviter les erreurs de cohérence de la mémoire.

110
jmort253

synchronized signifie que, dans un environnement multithread, un objet ayant synchronized méthode (s)/bloc (s) ne permet pas à deux threads d'accéder à la méthode synchronized (s)/block ( s) de code en même temps. Cela signifie qu'un thread ne peut pas lire tandis qu'un autre le met à jour.

Au lieu de cela, le second thread attendra que le premier thread termine son exécution. Le temps système est la rapidité, mais l’avantage est la cohérence garantie des données.

Si votre application est à thread unique, les blocs synchronized ne fournissent aucun avantage.

79
Codemwnci

Le mot clé synchronized permet à un thread d'obtenir un verrou lors de la saisie de la méthode, de sorte qu'un seul thread puisse exécuter la méthode en même temps (pour l'instance d'objet donnée, sauf s'il s'agit d'une méthode statique).

Cela s'appelle souvent rendre la classe thread-safe, mais je dirais que c'est un euphémisme. S'il est vrai que la synchronisation protège l'état interne du vecteur de la corruption, cela n'aide généralement pas beaucoup l'utilisateur du vecteur.

Considère ceci:

 if (vector.isEmpty()){
     vector.add(data);
 }

Même si les méthodes impliquées sont synchronisées, étant donné qu'elles sont verrouillées et déverrouillées individuellement, deux threads, malheureusement chronométrés, peuvent créer un vecteur avec deux éléments.

Donc, en réalité, vous devez également synchroniser votre code d'application.

La synchronisation au niveau de la méthode étant a) coûteuse lorsque vous n'en avez pas besoin et b) insuffisante lorsque vous avez besoin de synchronisation, il existe désormais des remplacements non synchronisés (ArrayList dans le cas de Vector).

Plus récemment, le package de concurrence a été publié, avec un certain nombre d'utilitaires intelligents qui prennent en charge les problèmes de multi-threading.

52
Thilo

Vue d'ensemble

Le mot clé synchronisé dans Java concerne la sécurité des threads, c'est-à-dire lorsque plusieurs threads lisent ou écrivent la même variable.
Cela peut arriver directement (en accédant à la même variable) ou indirectement (en utilisant une classe qui utilise une autre classe qui accède à la même variable).

Le mot clé synchronized est utilisé pour définir un bloc de code dans lequel plusieurs threads peuvent accéder à la même variable de manière sécurisée.

Plus profond

En termes de syntaxe, le mot clé synchronized prend un Object comme paramètre (appelé un objet verrou ), qui est ensuite suivi de un { block of code }.

  • Lorsque l'exécution rencontre ce mot-clé, le thread actuel tente de "verrouiller/acquérir/posséder" (à vous de choisir) l'objet de verrouillage et d'exécuter le bloc associé de code après l'acquisition du verrou.

  • Toutes les écritures dans les variables du bloc de code synchronisé sont visibles pour tous les autres threads qui exécutent le code de manière similaire dans un bloc de code synchronisé en utilisant le même objet verrou . .

  • Le verrou ne peut être verrouillé que par un seul thread à la fois. Tous les autres threads essayant d'acquérir le même objet verrou attendent (suspendent leur exécution). Le verrou sera libéré lorsque l'exécution quittera le bloc de code synchronisé.

Méthodes synchronisées:

L'ajout du mot clé synchronized à une définition de méthode est égal à l'ensemble du corps de la méthode encapsulé dans un bloc de code synchronisé avec l'objet verrou étant this (pour les méthodes d'instance) et ClassInQuestion.getClass() (pour les méthodes de classe) .

- La méthode d'instance est une méthode qui n'a pas de mot clé static.
- La méthode de classe est une méthode qui a le mot clé static.

Technique

Sans synchronisation, l'ordre dans lequel les lectures et les écritures se produisent n'est pas garanti, laissant éventuellement la variable avec des ordures.
(Par exemple, une variable pourrait se retrouver avec la moitié des bits écrits par un thread et la moitié des bits écrits par un autre thread, laissant la variable dans un état qu'aucun les threads ont essayé d'écrire, mais un fouillis combiné des deux.)

Il ne suffit pas de terminer une opération d'écriture dans un thread avant qu'un autre thread ne le lise (heure de l'horloge murale), car le matériel aurait pu mettre en cache la valeur de la variable et le thread en lecture verrait la valeur mise en cache au lieu de celle qui a été écrite il.

Conclusion

Ainsi, dans le cas de Java, vous devez suivre le modèle de mémoire Java pour vous assurer que les erreurs de threading ne se produisent pas.
En d'autres termes: utilisez la synchronisation, les opérations atomiques ou les classes qui les utilisent pour vous sous le capot

Sources

http://docs.Oracle.com/javase/specs/jls/se8/html/index.html
Spécification du langage Java®, 2015-02-13

24
Gima

Pensez-y comme à une sorte de tourniquet que vous pourriez trouver sur un terrain de football. Il y a des courants parallèles de personnes qui veulent entrer mais, au tourniquet, elles sont "synchronisées". Une seule personne à la fois peut passer. Tous ceux qui veulent passer vont le faire, mais ils devront peut-être attendre jusqu'à ce qu'ils puissent passer.

20
paul

Quel est le mot clé synchronisé?

Les threads communiquent principalement en partageant l'accès aux champs et aux objets référencés par les champs de référence. Cette forme de communication est extrêmement efficace, mais permet deux types d’erreur: et erreurs de cohérence de la mémoire . L'outil nécessaire pour éviter ces erreurs est la synchronisation.

Les méthodes ou les blocs synchronisés empêchent les interférences de thread et garantissent la cohérence des données. À tout moment, un seul thread peut accéder à un bloc ou à une méthode synchronisé ( section critique ) en acquérant un verrou. Les autres threads attendront la libération du verrou pour accéder à section critique .

Quand les méthodes sont-elles synchronisées?

Les méthodes sont synchronisées lorsque vous ajoutez synchronized à la définition ou à la déclaration de méthode. Vous pouvez également synchroniser un bloc de code particulier avec une méthode.

Qu'est-ce que cela signifie de manière grammaticale et logique?

Cela signifie qu'un seul thread peut accéder à section critique en acquérant un verrou. À moins que ce fil ne relâche ce verrou, tous les autres fils devront attendre pour acquérir un verrou. Ils ne peuvent pas entrer dans la section critique sans verrou.

Cela ne peut pas être fait avec une magie. Il est de la responsabilité du programmeur d’identifier la (les) section (s) critique (s) dans l’application et de la protéger en conséquence. Java fournit un cadre pour protéger votre application, mais le programmeur est responsable de l'emplacement et de ce que toutes les sections doivent être protégées.

Plus de détails de Java documentation page

Verrous et synchronisation intrinsèques:

La synchronisation est construite autour d'une entité interne appelée verrou intrinsèque ou verrou du moniteur. Les verrous intrinsèques jouent un rôle dans les deux aspects de la synchronisation: imposer un accès exclusif à l'état d'un objet et établir des relations entre événements qui sont essentielles à la visibilité.

Chaque objet est associé à un verrou intrinsèque . Par convention, un thread qui requiert un accès exclusif et cohérent aux champs d'un objet doit acquérir le verrou intrinsèque de l'objet avant d'y accéder, puis libérer le verrou intrinsèque lorsqu'il en a terminé avec eux.

Un thread est dit posséder le verrou intrinsèque entre le moment où il a acquis le verrou et libéré le verrou. Tant qu'un thread possède un verrou intrinsèque, aucun autre thread ne peut acquérir le même verrou. L'autre thread se bloquera lorsqu'il tentera d'acquérir le verrou.

Lorsqu'un thread libère un verrou intrinsèque, une relation passe-avant est établie entre cette action et toute acquisition ultérieure du même verrou.

Rendre les méthodes synchronisées a deux effets :

Premièrement, il n'est pas possible que deux invocations de méthodes synchronisées sur le même objet s'entrelacent.

Lorsqu'un thread exécute une méthode synchronisée pour un objet, tous les autres threads qui invoquent des méthodes synchronisées pour le même bloc d'objet (suspendre l'exécution) jusqu'à la fin du premier thread avec l'objet.

Deuxièmement, quand une méthode synchronisée se termine, elle établit automatiquement une relation passe-avant avec tout appel ultérieur d'une méthode synchronisée pour le même objet.

Cela garantit que les modifications apportées à l'état de l'objet sont visibles par tous les threads.

Recherchez d'autres alternatives à la synchronisation dans:

Evitez synchronisé (ceci) en Java?

16
Ravindra babu

Voici une explication de Les Java Tutorials .

Considérons le code suivant:

public class SynchronizedCounter {
    private int c = 0;

    public synchronized void increment() {
        c++;
    }

    public synchronized void decrement() {
        c--;
    }

    public synchronized int value() {
        return c;
    }
}

si count est une instance de SynchronizedCounter, la synchronisation de ces méthodes a alors deux effets:

  • Premièrement, il n'est pas possible que deux invocations de méthodes synchronisées sur le même objet s'entrelacent. Lorsqu'un thread exécute une méthode synchronisée pour un objet, tous les autres threads qui invoquent des méthodes synchronisées pour le même bloc d'objet (suspendre l'exécution) jusqu'à la fin du premier thread avec l'objet.
  • Deuxièmement, quand une méthode synchronisée se termine, elle établit automatiquement une relation passe-avant avec tout appel ultérieur d'une méthode synchronisée pour le même objet. Cela garantit que les modifications apportées à l'état de l'objet sont visibles par tous les threads.
10
Shahryar Saljoughi

À ma connaissance, synchronisé signifie en principe que le compilateur écrit un moniteur.enter et un moniteur.exit autour de votre méthode. En tant que tel, il peut être thread-safe en fonction de son utilisation (ce que je veux dire, c'est que vous pouvez écrire un objet avec des méthodes synchronisées qui ne sont pas threadsafe en fonction de ce que fait votre classe).

8
Spence

Synchronized normal method équivalent à Synchronized statement (utilisez ceci)

class A {
    public synchronized void methodA() {
        // all function code
    }

    equivalent to

    public void methodA() {
        synchronized(this) {
             // all function code
        }
    } 
}

Synchronized static method équivalent à Synchronized statement (classe d'utilisation)

class A {
    public static synchronized void methodA() {
        // all function code
    }

    equivalent to

    public void methodA() {
        synchronized(A.class) {
             // all function code
        }
    } 
}

Instruction synchronisée (à l'aide d'une variable)

class A {
    private Object lock1 = new Object();

    public void methodA() {
        synchronized(lock1 ) {
             // all function code
        }
    } 
}

Pour synchronized, nous avons à la fois Synchronized Methods et Synchronized Statements. Cependant, Synchronized Methods est similaire à Synchronized Statements, nous avons donc besoin de comprendre Synchronized Statements.

=> En gros, nous aurons

synchronized(object or class) { // object/class use to provides the intrinsic lock
   // code 
}

Voici 2 pense que cela aide à comprendre synchronized

  • Chaque objet/classe est associée à un intrinsic lock.
  • Lorsqu'un thread appelle un synchronized statement, il acquiert automatiquement l'objet intrinsic lock pour ce synchronized statement's et le libère au retour de la méthode. Tant qu'un thread possède un intrinsic lock, AUCUN autre thread ne peut acquérir le MÊME lock => thread safe.

=> Quand un thread A appelle synchronized(this){// code 1} => tout le code de bloc (dans la classe) où ont synchronized(this) et tout synchronized normal method (dans la classe) est verrouillé car MÊME verrouiller. Il sera exécuté après thread A _ unlock ("// code 1" terminé).

Ce comportement est similaire à synchronized(a variable){// code 1} ou synchronized(class).

SAME LOCK => verrouiller (ne dépend pas de quelle méthode? Ni de quelles déclarations?)

Utiliser une méthode synchronisée ou des instructions synchronisées?

Je préfère synchronized statements parce qu'il est plus extensible. Par exemple, à l'avenir, vous n'aurez besoin que d'une partie synchronisée de la méthode. Exemple, vous avez 2 méthodes synchronisées et il n’a aucune relation entre elles , cependant, lorsqu'un thread exécute une méthode, il bloque l’autre. méthode (il peut empêcher en utilisant synchronized(a variable)).

Cependant, appliquer une méthode synchronisée est simple et le code a l'air simple. Pour certaines classes, il n'y a qu'une seule méthode synchronisée, ou toutes les méthodes synchronisées de la classe sont pertinentes entre elles => nous pouvons utiliser synchronized method pour rendre le code plus court et facile à comprendre.

Remarque

(ce n'est pas pertinent pour beaucoup de synchronized, c'est la différence entre object et class ou no-static et static).

  • Lorsque vous utilisez synchronized ou une méthode normale ou synchronized(this) ou synchronized(non-static variable), il est synchronisé sur la base de chaque instance d'objet.
  • Lorsque vous utilisez synchronized ou une méthode statique ou synchronized(class) ou synchronized(static variable), la base sera synchronisée

Référence

https://docs.Oracle.com/javase/tutorial/essential/concurrency/syncmeth.htmlhttps://docs.Oracle.com/javase/tutorial/essential/concurrency/ locksync.html

J'espère que ça aide

8
Phan Van Linh

Ce qui manque aux autres réponses est un aspect important: les barrières de mémoire . La synchronisation des threads consiste essentiellement en deux parties: sérialisation et visibilité. Je conseille à tout le monde de rechercher Google pour "jvm memory barrier", car il s'agit d'un sujet non trivial et extrêmement important (si vous modifiez des données partagées accessibles par plusieurs threads). Cela fait, je vous conseille de consulter les classes du package Java.util.concurrent qui permettent d'éviter l'utilisation de la synchronisation explicite, ce qui permet de garder les programmes simples et efficaces, voire d'éviter les blocages.

Un tel exemple est ConcurrentLinkedDeque . Associé au modèle de commande , il permet de créer des threads de travail hautement efficaces en plaçant les commandes dans la file d'attente simultanée - aucune synchronisation explicite nécessaire, aucune impasse possible, aucun sommeil () explicite nécessaire , il suffit d’interroger la file d’appel en appelant take ().

En bref: "synchronisation de la mémoire" se produit implicitement lorsque vous démarrez un fil, un fil se termine, vous lisez une variable volatile, vous déverrouillez un moniteur (laissez synchronized block/function) etc. etc. Cette "synchronisation" affecte (dans un sens, "chasse") tout écrit avant cette action particulière. Dans le cas de ConcurrentLinkedDeque susmentionné, la documentation "dit":

Effets de cohérence de la mémoire: comme pour les autres collections simultanées, actions d'un thread avant de placer un objet dans un ConcurrentLinkedDeque actions happen-before consécutives à l'accès ou à la suppression de cet élément de ConcurrentLinkedDeque dans un autre fil.

Ce comportement implicite est un aspect quelque peu pernicieux, car la plupart des programmeurs Java sans grande expérience prendront tout simplement ce qui leur est donné. Et puis soudain, nous tombons sur ce fil après que Java ne fasse pas ce qu’il est "supposé" faire en production où la charge de travail est différente - et il est assez difficile de tester les problèmes d’accès simultané.

5
user1050755

Synchronisé signifie simplement que plusieurs threads, s'ils sont associés à un seul objet, peuvent empêcher la lecture et l'écriture modifiés si un bloc synchronisé est utilisé sur un objet particulier. Pour vous donner plus de clarté, prenons un exemple:

class MyRunnable implements Runnable {
    int var = 10;
    @Override
    public void run() {
        call();
    }

    public void call() {
        synchronized (this) {
            for (int i = 0; i < 4; i++) {
                var++;
                System.out.println("Current Thread " + Thread.currentThread().getName() + " var value "+var);
            }
        }
    }
}

public class MutlipleThreadsRunnable {
    public static void main(String[] args) {
        MyRunnable runnable1 = new MyRunnable();
        MyRunnable runnable2 = new MyRunnable();
        Thread t1 = new Thread(runnable1);
        t1.setName("Thread -1");
        Thread t2 = new Thread(runnable2);
        t2.setName("Thread -2");
        Thread t3 = new Thread(runnable1);
        t3.setName("Thread -3");
        t1.start();
        t2.start();
        t3.start();
    }
}

Nous avons créé deux objets de classe MyRunnable, runnable1 étant partagé avec le thread 1 et le thread 3 & runnable2 étant partagé avec le thread 2 uniquement. Maintenant, lorsque t1 et t3 démarrent sans utilisation synchronisée, la sortie PFB suggère que les threads 1 et 3 affectent simultanément la valeur var, où, pour le thread 2, var a sa propre mémoire.

Without Synchronized keyword

    Current Thread Thread -1 var value 11
    Current Thread Thread -2 var value 11
    Current Thread Thread -2 var value 12
    Current Thread Thread -2 var value 13
    Current Thread Thread -2 var value 14
    Current Thread Thread -1 var value 12
    Current Thread Thread -3 var value 13
    Current Thread Thread -3 var value 15
    Current Thread Thread -1 var value 14
    Current Thread Thread -1 var value 17
    Current Thread Thread -3 var value 16
    Current Thread Thread -3 var value 18

Avec Synchronzied, le thread 3 attend que le thread 1 se termine dans tous les scénarios. Deux verrous sont acquis, l'un sur runnable1 partagé par le thread 1 et le thread 3 et l'autre sur runnable2 partagé par le thread 2 uniquement.

Current Thread Thread -1 var value 11
Current Thread Thread -2 var value 11
Current Thread Thread -1 var value 12
Current Thread Thread -2 var value 12
Current Thread Thread -1 var value 13
Current Thread Thread -2 var value 13
Current Thread Thread -1 var value 14
Current Thread Thread -2 var value 14
Current Thread Thread -3 var value 15
Current Thread Thread -3 var value 16
Current Thread Thread -3 var value 17
Current Thread Thread -3 var value 18
4
paras4all

simple synchronisé signifie que deux threads ne peuvent pas accéder simultanément au bloc/à la méthode. Quand nous disons qu'un bloc/une méthode d'une classe est synchronisé, cela signifie qu'un seul thread peut y accéder à la fois. En interne, le thread qui tente d'y accéder commence par verrouiller cet objet et tant que ce verrou n'est pas disponible, aucun autre thread ne peut accéder aux méthodes/blocs synchronisés de cette instance de la classe.

Notez qu'un autre thread peut accéder à une méthode du même objet qui n'est pas définie pour être synchronisée. Un thread peut libérer le verrou en appelant

Object.wait()
2
Aniket Thakur