JavaRush /Blog Java /Random-FR /Synchronisation des threads. Opérateur synchronisé en Jav...

Synchronisation des threads. Opérateur synchronisé en Java

Publié dans le groupe Random-FR
Bonjour! Aujourd'hui, nous continuerons à examiner les fonctionnalités de la programmation multithread et à parler de synchronisation des threads.
Synchronisation des threads.  Opérateur synchronisé - 1
Qu’est-ce que la « synchronisation » ? En dehors du domaine de la programmation, cela fait référence à une sorte de configuration permettant à 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 personnel sur un site Web peut être synchronisé avec des comptes de réseaux sociaux afin de se connecter avec ceux-ci. La synchronisation des threads a une signification similaire : elle configure la manière dont les threads interagissent les uns avec les autres. Dans les conférences précédentes, nos fils vivaient et travaillaient séparément les uns des autres. L'un comptait quelque chose, le deuxième dormait, le troisième affichait quelque chose sur la console, mais ils n'interagissaient pas les uns avec les autres. Dans les programmes réels, de telles situations sont rares. Plusieurs threads peuvent travailler activement, par exemple, avec le même ensemble de données et y modifier quelque chose. Cela crée des problèmes. Imaginez que plusieurs threads écrivent du texte au même emplacement, comme un fichier texte ou la console. Ce fichier ou console devient dans ce cas une ressource partagée. Les threads ne connaissent pas l’existence des autres, ils notent donc simplement tout ce qu’ils peuvent gérer dans le temps que leur alloue le planificateur de threads. Dans une récente leçon du cours, nous avons eu un exemple de ce à quoi cela aboutirait, rappelons-le : Synchronisation des threads.  Opérateur synchronisé - 2la raison réside dans le fait que les threads travaillaient avec une ressource partagée, la console, sans coordonner leurs actions entre eux. Si le planificateur de threads a alloué du temps au Thread-1, il écrit immédiatement tout sur la console. Ce que d’autres threads ont déjà réussi à écrire ou n’ont pas réussi à écrire n’a pas d’importance. Le résultat, comme vous pouvez le constater, est désastreux. Par conséquent, dans la programmation multithread, un concept spécial de mutex a été introduit (de l'anglais "mutex", "mutual exclusion" - "mutual exclusion") . Le but d'un mutex est de fournir un mécanisme permettant qu'un seul thread ait accès à un objet à un moment donné. Si Thread-1 a acquis le mutex de l'objet A, les autres threads n'y auront pas accès pour y modifier quoi que ce soit. Jusqu'à ce que le mutex de l'objet A soit libéré, les threads restants seront obligés d'attendre. Exemple concret : imaginez que vous et 10 autres inconnus participez à une formation. Vous devez exprimer vos idées et discuter de quelque chose à tour de rôle. Mais, comme vous vous voyez pour la première fois, pour ne pas vous interrompre constamment et ne pas sombrer dans le brouhaha, vous utilisez la règle du « ballon qui parle » : une seule personne peut parler, celle qui a le ballon dedans. ses mains. De cette façon, la discussion s'avère adéquate et fructueuse. Ainsi, un mutex, en substance, est une telle boule. Si le mutex d'un objet est entre les mains d'un thread, les autres threads ne pourront pas accéder à l'objet. Vous n'avez rien à faire pour créer un mutex : il est déjà intégré à la classe Object, ce qui signifie que chaque objet Java l'a.

Comment fonctionne l'opérateur synchronisé en Java

Faisons connaissance avec un nouveau mot-clé - synchronisé . Il marque une certaine partie de notre code. Si un bloc de code est marqué avec le mot-clé synchronisé, cela signifie que le bloc ne peut être exécuté que par un seul thread à la fois. La synchronisation peut être mise en œuvre de différentes manières. Par exemple, créez une méthode synchronisée entière :
public synchronized void doSomething() {

   //...method logic
}
Ou écrivez un bloc de code où la synchronisation est effectuée sur un objet :
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       //...some logic available to all threads

       synchronized (obj) {

           //logic that is only available to one thread at a time
       }
   }
}
Le sens est simple. Si un thread entre dans un bloc de code marqué du mot synchronisé, il acquiert instantanément le mutex de l'objet, et tous les autres threads qui tentent 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.  Opérateur synchronisé - 3D'ailleurs! Dans les cours magistraux, vous avez déjà vu des exemples de synchronisation, mais ils étaient différents :
public void swap()
{
   synchronized (this)
   {
       //...method logic
   }
}
Le sujet est nouveau pour vous et bien sûr, il y aura une confusion avec la syntaxe au début. Par conséquent, rappelez-vous tout de suite pour ne pas vous tromper plus tard dans les méthodes d'écriture. Ces deux méthodes d’écriture signifient 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é immédiatement après avoir entré la méthode. Il est synchronisé par object this, c'est-à-dire par l'objet courant. Et dans le deuxième exemple, vous mettez le mot synchronisé sur toute la méthode. Il n'est plus nécessaire d'indiquer explicitement un objet sur lequel la synchronisation est effectuée. Une fois qu'une méthode entière est marquée d'un mot, cette méthode sera automatiquement synchronisée pour tous les objets de la classe. N'entrons pas dans la discussion sur la meilleure méthode. Pour l'instant, choisissez ce que vous préférez :) L'essentiel est de se rappeler : vous ne pouvez déclarer une méthode synchronisée que lorsque toute la logique qu'elle contient est exécutée par un thread en même temps. Par exemple, dans ce cas, doSomething()ce serait une erreur de synchroniser la méthode :
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       //...some logic available to all threads

       synchronized (obj) {

           //logic that is only available to one thread at a time
       }
   }
}
Comme vous pouvez le constater, une partie de la méthode contient une logique pour laquelle la synchronisation n'est pas requise. Le code qu'il contient peut être exécuté par plusieurs threads simultanément et tous les emplacements critiques sont alloués à un bloc synchronisé distinct. Et un instant. Regardons au microscope notre exemple tiré de la conférence avec l'échange de noms :
public void swap()
{
   synchronized (this)
   {
       //...method logic
   }
}
Attention : la synchronisation s'effectue à l'aide de this. C'est-à-dire pour un objet spécifique MyClass. Imaginez que nous ayons 2 threads ( Thread-1et Thread-2) et un seul objet MyClass myClass. Dans ce cas, si Thread-1la méthode est appelée myClass.swap(), le mutex de l'objet sera occupé et Thread-2lorsque vous tenterez de l'appeler, myClass.swap()il se bloquera en attendant que le mutex se libère. Si nous avons 2 threads et 2 objets MyClass- myClass1et myClass2- sur des objets différents, nos threads peuvent facilement exécuter simultanément des méthodes synchronisées. Le premier thread fait :
myClass1.swap();
Le deuxième fait :
myClass2.swap();
Dans ce cas, le mot-clé synchronisé à l'intérieur de la méthode swap()n'affectera pas le fonctionnement du programme, puisque la synchronisation est effectuée sur un objet spécifique. Et dans ce dernier cas, nous avons 2 objets, donc les threads ne se créent pas de problèmes entre eux. Après tout, deux objets ont 2 mutex différents, et leur capture ne dépend pas l'une de l'autre.

Fonctionnalités de synchronisation dans les méthodes statiques

Mais que se passe-t-il si vous devez synchroniser une méthode statique ?
class MyClass {
   private static String name1 = "Olya";
   private static String name2 = "Lena";

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

}
On ne sait pas exactement ce qui servira de mutex dans ce cas. Après tout, nous avons déjà décidé que chaque objet possède un mutex. Mais le problème c’est que pour appeler une méthode statique MyClass.swap()on n’a pas besoin d’objets : la méthode est statique ! Alors, quelle est la prochaine étape ? :/ En fait, cela ne pose aucun problème. Les créateurs de Java se sont occupés de tout :) Si la méthode qui contient la logique « multithread » critique est statique, la synchronisation sera effectuée par classe. Pour plus de clarté, le code ci-dessus peut être réécrit comme suit :
class MyClass {
   private static String name1 = "Olya";
   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 par vous-même : puisqu'il n'y a pas d'objets, alors le mécanisme de synchronisation doit d'une manière ou d'une autre être « câblé » dans les classes elles-mêmes. C’est comme ça : vous pouvez également synchroniser entre les classes.
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION