CodeGym /Blog Java /Random-FR /Synchronisation des threads. L'opérateur synchronisé
Auteur
Jesse Haniel
Lead Software Architect at Tribunal de Justiça da Paraíba

Synchronisation des threads. L'opérateur synchronisé

Publié dans le groupe Random-FR
Salut! Aujourd'hui, nous allons continuer à considérer les fonctionnalités de la programmation multithread et parler de la synchronisation des threads. Synchronisation des threads.  L'opérateur synchronisé - 1

Qu'est-ce que la synchronisation en Java ?

En dehors du domaine de la programmation, cela implique un arrangement qui permet à deux appareils ou programmes de fonctionner ensemble. Par exemple, un smartphone et un ordinateur peuvent être synchronisés avec un compte Google, et un compte de site Web peut être synchronisé avec des comptes de réseaux sociaux afin que vous puissiez les utiliser pour vous connecter. La synchronisation des fils a une signification similaire : c'est un arrangement dans lequel les fils interagissent avec l'un l'autre. Dans les leçons précédentes, nos threads vivaient et fonctionnaient séparément les uns des autres. L'un a effectué un calcul, un deuxième a dormi et un troisième a affiché quelque chose sur la console, mais ils n'ont pas interagi. Dans les programmes réels, de telles situations sont rares. Plusieurs threads peuvent travailler activement avec et modifier le même ensemble de données. Cela crée des problèmes. Imaginez plusieurs threads écrivant du texte au même endroit, par exemple, dans un fichier texte ou la console. Dans ce cas, le fichier ou la console devient une ressource partagée. Les threads ignorent l'existence les uns des autres, ils écrivent donc simplement tout ce qu'ils peuvent dans le temps qui leur est imparti par le planificateur de threads. Dans une leçon récente, nous avons vu un exemple d'où cela mène. Rappelons-le maintenant : Synchronisation des threads.  L'opérateur synchronisé - 2La raison réside dans le fait que les threads travaillent avec une ressource partagée (la console) sans coordonner leurs actions les unes avec les autres. Si le planificateur de threads alloue du temps à Thread-1, il écrit instantanément tout sur la console. Ce que les autres threads ont ou n'ont pas déjà réussi à écrire n'a pas d'importance. Le résultat, comme vous pouvez le voir, est déprimant. C'est pourquoi ils ont introduit un concept spécial, le mutex (exclusion mutuelle) , dans la programmation multithread. Le but d'un mutexest de fournir un mécanisme pour qu'un seul thread ait accès à un objet à un moment donné. Si Thread-1 acquiert le mutex de l'objet A, les autres threads ne pourront pas accéder à l'objet ni le modifier. Les autres threads doivent attendre que le mutex de l'objet A soit libéré. Voici un exemple tiré de la vie : imaginez que vous et 10 autres étrangers participez à un exercice. À tour de rôle, vous devez exprimer vos idées et discuter de quelque chose. Mais parce que vous vous voyez pour la première fois, pour ne pas vous interrompre constamment et vous mettre en colère, vous utilisez un « ballon parlant » : seul celui qui a le ballon peut parler. De cette façon, vous finissez par avoir une bonne et fructueuse discussion. Essentiellement, la balle est un mutex. Si le mutex d'un objet est entre les mains d'un thread, les autres threads ne peuvent pas fonctionner avec l'objet.Objectclasse, ce qui signifie que chaque objet en Java en a une.

Fonctionnement de l'opérateur synchronisé

Apprenons à connaître un nouveau mot-clé : synchronized . Il est utilisé pour marquer un certain bloc de code. Si un bloc de code est marqué avec le synchronizedmot-clé, alors ce bloc ne peut être exécuté que par un thread à la fois. La synchronisation peut être mise en œuvre de différentes manières. Par exemple, en déclarant toute une méthode à synchroniser :

public synchronized void doSomething() {

   // ...Method logic
}
Ou écrivez un bloc de code où la synchronisation est effectuée à l'aide d'un objet :

public class Main {

   private Object obj = new Object();

   public void doSomething() {

       // ...Some logic available simultaneously to all threads

       synchronized (obj) {

           // Logic available to just one thread at a time
       }
   }
}
Le sens est simple. Si un thread entre dans le bloc de code marqué du synchronizedmot-clé, il capture instantanément le mutex de l'objet et tous les autres threads essayant d'entrer dans le même bloc ou la même méthode sont obligés d'attendre que le thread précédent termine son travail et libère le moniteur. Synchronisation des threads.  L'opérateur synchronisé - 3D'ailleurs! Pendant le cours, vous avez déjà vu des exemples de synchronized, mais ils semblaient différents :

public void swap()
{
   synchronized (this)
   {
       // ...Method logic
   }
}
Le sujet est nouveau pour vous. Et, bien sûr, il y aura confusion avec la syntaxe. Alors, mémorisez-le tout de suite pour éviter d'être confondu plus tard par les différentes manières de l'écrire. Ces deux façons de l'écrire veulent dire la même chose :

public void swap() {

   synchronized (this)
   {
       // ...Method logic
   }
}


public synchronized void swap() {

   }
}
Dans le premier cas, vous créez un bloc de code synchronisé dès que vous entrez dans la méthode. Il est synchronisé par l' thisobjet, c'est-à-dire l'objet courant. Et dans le deuxième exemple, vous appliquez le synchronizedmot-clé à l'ensemble de la méthode. Cela rend inutile d'indiquer explicitement l'objet utilisé pour la synchronisation. Étant donné que la méthode entière est marquée avec le mot-clé, la méthode sera automatiquement synchronisée pour toutes les instances de la classe. Nous ne nous lancerons pas dans une discussion sur la meilleure manière. Pour l'instant, choisissez la méthode qui vous convient le mieux :) L'essentiel est de se rappeler : vous pouvez déclarer une méthode synchronisée uniquement lorsque toute sa logique est exécutée par un thread à la fois. Par exemple, ce serait une erreur de doSomething()synchroniser la méthode suivante :

public class Main {

   private Object obj = new Object();

   public void doSomething() {

       // ...Some logic available simultaneously to all threads

       synchronized (obj) {

           // Logic available to just one thread at a time
       }
   }
}
Comme vous pouvez le voir, une partie de la méthode contient une logique qui ne nécessite pas de synchronisation. Ce code peut être exécuté par plusieurs threads en même temps, et tous les endroits critiques sont séparés dans un synchronizedbloc séparé. Et encore une chose. Examinons de près notre exemple de la leçon avec l'échange de noms :

public void swap()
{
   synchronized (this)
   {
       // ...Method logic
   }
}
Remarque : la synchronisation est effectuée à l'aide dethis. C'est-à-dire en utilisant unMyClassobjet spécifique. Supposons que nous ayons 2 threads (Thread-1etThread-2) et un seulMyClass myClassobjet. Dans ce cas, siThread-1appelle lamyClass.swap()méthode, le mutex de l'objet sera occupé et lors de la tentative d'appel, lamyClass.swap()méthodeThread-2se bloquera en attendant que le mutex soit libéré. Si nous aurons 2 threads et 2MyClassobjets (myClass1etmyClass2), nos threads pourront facilement exécuter simultanément les méthodes synchronisées sur différents objets. Le premier thread exécute ceci :

myClass1.swap();
Le second exécute ceci :

myClass2.swap();
Dans ce cas, le synchronizedmot clé à l'intérieur de la swap()méthode n'affectera pas le fonctionnement du programme, puisque la synchronisation est effectuée à l'aide d'un objet spécifique. Et dans ce dernier cas, nous avons 2 objets. Ainsi, les threads ne créent pas de problèmes les uns pour les autres. Après tout, deux objets ont 2 mutex différents, et l'acquisition de l'un est indépendante de l'acquisition de l'autre .

Particularités de la synchronisation dans les méthodes statiques

Mais que faire si vous avez besoin de synchroniser une méthode statique ?

class MyClass {
   private static String name1 = "Ally";
   private static String name2 = "Lena";

   public static synchronized void swap() {
       String s = name1;
       name1 = name2;
       name2 = s;
   }

}
Le rôle que le mutex jouera ici n'est pas clair. Après tout, nous avons déjà déterminé que chaque objet a un mutex. Mais le problème est que nous n'avons pas besoin d'objets pour appeler la MyClass.swap()méthode : la méthode est statique ! Alors, quelle est la prochaine étape ? :/ Il n'y a en fait aucun problème ici. Les créateurs de Java se sont occupés de tout :) Si une méthode qui contient une logique concurrente critique est statique, alors la synchronisation est effectuée au niveau de la classe. Pour plus de clarté, nous pouvons réécrire le code ci-dessus comme suit :

class MyClass {
   private static String name1 = "Ally";
   private static String name2 = "Lena";

   public static void swap() {

       synchronized (MyClass.class) {
           String s = name1;
           name1 = name2;
           name2 = s;
       }
   }

}
En principe, vous auriez pu y penser vous-même : puisqu'il n'y a pas d'objets, le mécanisme de synchronisation doit d'une manière ou d'une autre être intégré à la classe elle-même. Et c'est comme ça : on peut utiliser des classes pour se synchroniser.
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION