JavaRush /Blog Java /Random-FR /Quelle est la différence entre un mutex, un moniteur et u...

Quelle est la différence entre un mutex, un moniteur et un sémaphore

Publié dans le groupe Random-FR
Bonjour! En étudiant le multithreading dans JavaRush, vous êtes souvent tombé sur les concepts de « mutex » et de « moniteur ». Pouvez-vous maintenant, sans jeter un coup d’œil, expliquer en quoi ils diffèrent ? :) Quelle est la différence entre un mutex, un moniteur et un sémaphore - 1Si vous pouviez, bravo ! Si ce n'est pas le cas (et cela arrive le plus souvent), ce n'est pas étonnant. Les notions de « mutex » et de « moniteur » sont en effet liées. De plus, en lisant des conférences et en regardant des vidéos sur le multithreading sur des ressources externes sur Internet, vous rencontrerez un autre concept similaire : le « sémaphore ». Sa fonctionnalité est également largement similaire à celle d’un moniteur et d’un mutex. Comprenons donc ces trois termes, regardons quelques exemples et enfin organisons dans nos têtes la compréhension en quoi ils diffèrent les uns des autres :)

Mutex

Un mutex est un objet spécial pour synchroniser les threads. Il est "attaché" à chaque objet en Java - vous le savez déjà :) Peu importe que vous utilisiez des classes standard ou que vous créiez vos propres classes, par exemple, Catet Dog: tous les objets de toutes les classes ont un mutex . Le nom « mutex » vient de l'anglais « MUTual EXclusion » - « exclusion mutuelle », et cela reflète parfaitement son objectif. Comme nous l'avons dit dans l'une des conférences précédentes, la tâche d'un mutex est de fournir un mécanisme tel qu'un seul thread ait accès à un objet à un certain moment . Une analogie populaire pour un mutex dans la vraie vie est « l’exemple des toilettes ». Lorsqu’une personne entre dans les toilettes, elle verrouille la porte de l’intérieur. Les toilettes agissent comme un objet accessible par plusieurs threads. La serrure de la porte des toilettes est le rôle d'un mutex, et la file d'attente des personnes à l'extérieur est le rôle des fils. La serrure de la porte est un mutex des toilettes : elle garantit qu'une seule personne à la fois peut être à l'intérieur. Quelle est la différence entre un mutex, un moniteur et un sémaphore - 2En d’autres termes, un seul thread à la fois peut travailler sur des ressources partagées. Les tentatives d'autres threads (personnes) pour accéder aux ressources occupées échoueront. Un mutex possède plusieurs fonctionnalités importantes. Premièrement , seuls deux états sont possibles : « libre » et « occupé ». Cela facilite la compréhension de son fonctionnement : des parallèles peuvent être établis avec les variables booléennes vrai/faux ou le système de nombres binaires 1/0. Deuxièmement , les États ne peuvent pas être contrôlés directement. Il n'existe aucun mécanisme en Java qui vous permettrait de prendre explicitement un objet, d'obtenir son mutex et de lui attribuer le statut souhaité. En d'autres termes, vous ne pouvez pas faire quelque chose comme :
Object myObject = new Object();
Mutex mutex = myObject.getMutex();
mutex.free();
Ainsi, le mutex de l'objet ne peut pas être libéré. Seule la machine Java y a un accès direct. Les programmeurs travaillent avec des mutex à l'aide d'outils linguistiques.

Moniteur

Un moniteur est un « module complémentaire » supplémentaire à un mutex. En fait, le moniteur est un morceau de code « invisible » pour le programmeur . En parlant du mutex plus tôt, nous avons donné un exemple simple :
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
       }
   }
}
Dans le bloc de code marqué du mot synchronized, le mutex de notre objet est capturé obj. D’accord, la capture a lieu, mais comment exactement le « mécanisme de défense » est-il réalisé ? synchronizedPourquoi les autres fils de discussion ne peuvent-ils pas entrer dans le bloc lorsqu'ils voient un mot ? C'est le moniteur qui crée le mécanisme de protection ! Le compilateur convertit le mot synchronizeden plusieurs morceaux de code spéciaux. Revenons encore une fois à notre exemple avec la méthode doSomething()et ajoutons-y :
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       //...some logic available to all threads

       //logic that is only available to one thread at a time
       synchronized (obj) {

           /*выполнить важную работу, при которой доступ к an objectу
           должен быть только у одного потока*/
           obj.someImportantMethod();
       }
   }
}
Voici ce qui se passera « sous le capot » de notre programme une fois que le compilateur aura converti ce code :
public class Main {

   private Object obj = new Object();

   public void doSomething() throws InterruptedException {

       //...some logic available to all threads

       //логика, которая одновременно доступна только для одного потока:

       /*до тех пор, пока мьютекс an object занят -
       любой другой поток (кроме того, который его захватил), спит*/
       while (obj.getMutex().isBusy()) {
           Thread.sleep(1);
       }

       //пометить мьютекс an object How занятый
       obj.getMutex().isBusy() = true;

       /*выполнить важную работу, при которой доступ к an objectу
       должен быть только у одного потока*/
       obj.someImportantMethod();

       //освободить мьютекс an object
       obj.getMutex().isBusy() = false;
   }
}
Bien entendu, l’exemple n’est pas réel. Ici, en utilisant du code de type Java, nous avons essayé de refléter ce qui se passe actuellement à l'intérieur de la machine Java. Cependant, ce pseudocode donne une bonne compréhension de ce qui se passe réellement avec l'objet et les threads à l'intérieur du bloc synchronizedet comment le compilateur convertit ce mot en plusieurs commandes « invisibles » pour le programmeur. Essentiellement, surveiller en Java s'exprime à l'aide du motsynchronized . Tout le code qui est apparu à la place du mot synchronizeddans le dernier exemple est le moniteur.

Sémaphore

Un autre mot que vous rencontrez lorsque vous étudiez le multithreading par vous-même est « sémaphore ». Voyons ce que c'est et en quoi il diffère d'un moniteur et d'un mutex. Un sémaphore est un moyen de synchroniser l'accès à une ressource. Sa particularité est qu'il utilise un compteur lors de la création d'un mécanisme de synchronisation. Le compteur nous indique combien de threads peuvent accéder simultanément à une ressource partagée. Quelle est la différence entre un mutex, un moniteur et un sémaphore - 3Les sémaphores en Java sont représentés par la classe Semaphore. Lors de la création d'objets sémaphores, nous pouvons utiliser les constructeurs suivants :
Semaphore(int permits)
Semaphore(int permits, boolean fair)
On passe au constructeur :
  • int permits— valeur initiale et maximale du compteur. Autrement dit, combien de threads peuvent accéder simultanément à une ressource partagée ;

  • boolean fair- pour établir l'ordre dans lequel les threads recevront l'accès. Si fair= true , l'accès est accordé aux threads en attente dans l'ordre dans lequel ils l'ont demandé. Si c'est false , l'ordre sera déterminé par le planificateur de threads.

Un exemple classique de l'utilisation des sémaphores est le problème des philosophes du déjeuner .
Quelle est la différence entre un mutex, un moniteur et un sémaphore - 4
Nous allons simplifier un peu ses termes pour une meilleure compréhension. Imaginez que nous ayons 5 philosophes qui ont besoin de déjeuner. En même temps, nous avons une table et pas plus de deux personnes peuvent y être présentes en même temps. Notre tâche est de nourrir tous les philosophes. Ni l’un ni l’autre ne doivent avoir faim, ni se « bloquer » lorsqu’ils tentent de se mettre à table (il faut éviter les impasses). Voici à quoi ressemblera notre cours de philosophie :
class Philosopher extends Thread {

   private Semaphore sem;

   // поел ли философ
   private boolean full = false;

   private String name;

   Philosopher(Semaphore sem, String name) {
       this.sem=sem;
       this.name=name;
   }

   public void run()
   {
       try
       {
           // если философ еще не ел
           if (!full) {
               //Запрашиваем у семафора разрешение на выполнение
               sem.acquire();
               System.out.println (name + " садится за стол");

               // философ ест
               sleep(300);
               full = true;

               System.out.println (name + " поел! Он выходит из-за стола");
               sem.release();

               // философ ушел, освободив место другим
               sleep(300);
           }
       }
       catch(InterruptedException e) {
           System.out.println ("What-то пошло не так!");
       }
   }
}
Et voici le code pour exécuter notre programme :
public class Main {

   public static void main(String[] args) {

       Semaphore sem = new Semaphore(2);
       new Philosopher(sem,"Сократ").start();
       new Philosopher(sem,"Платон").start();
       new Philosopher(sem,"Аристотель").start();
       new Philosopher(sem,"Фалес").start();
       new Philosopher(sem,"Пифагор").start();
   }
}
Nous avons créé un sémaphore comptant 2 pour satisfaire la condition selon laquelle seuls deux philosophes peuvent manger en même temps. Autrement dit, seuls deux threads peuvent fonctionner simultanément, car notre classe Philosopherest héritée de Thread! Les méthodes acquire()et release()la classe Semaphorecontrôlent son compteur d'autorisations. La méthode acquire()demande l'autorisation d'accéder à une ressource à partir du sémaphore. Si compteur > 0, la permission est accordée et le compteur est décrémenté de 1. La méthode release()"libère" la permission précédemment accordée et la renvoie au compteur (en incrémentant le compteur d'octroi du sémaphore de 1). Qu'obtenons-nous lorsque nous exécutons le programme ? Le problème est-il résolu : nos philosophes se battront-ils en attendant leur tour ? :) Voici le résultat de la console que nous avons reçu : Socrate s'assoit à table Platon s'assoit à la table que Socrate a mangé ! Il quitte la table, Platon a mangé ! Il quitte la table Aristote s'assoit à la table Pythagore s'assoit à la table qu'Aristote a mangé ! Il quitte la table que Pythagore a mangée ! Il quitte la table . Thalès s'assoit à la table que Thalès a mangée ! Il quitte la table, nous avons réussi ! Et même si Thalès a dû dîner seul, je pense qu'il ne nous en veut pas :) Vous avez peut-être remarqué certaines similitudes entre un mutex et un sémaphore. En général, ils ont le même objectif : synchroniser l'accès à une ressource. Quelle est la différence entre un mutex, un moniteur et un sémaphore - 5La seule différence est que le mutex d’un objet ne peut être acquis que par un thread à la fois, alors que dans le cas d’un sémaphore, un compteur de threads est utilisé, et plusieurs d’entre eux peuvent accéder à la ressource en même temps. Et ce n'est pas seulement une similitude fortuite :) En fait, un mutex est un sémaphore à une seule place . C'est-à-dire qu'il s'agit d'un sémaphore dont le compteur est initialement mis à 1. On l'appelle aussi « sémaphore binaire » car son compteur ne peut avoir que 2 valeurs - 1 (« libre ») et 0 (« occupé »). C'est tout! Comme vous pouvez le constater, tout s'est avéré moins déroutant :) Désormais, si vous souhaitez étudier plus en détail le sujet du multithreading sur Internet, il vous sera un peu plus facile de naviguer dans les concepts. Rendez-vous dans les prochains cours !
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION