CodeGym /Java-Blog /Random-DE /Unterschied zwischen einem Mutex, einem Monitor und einem...
Autor
Oleksandr Miadelets
Head of Developers Team at CodeGym

Unterschied zwischen einem Mutex, einem Monitor und einem Semaphor

Veröffentlicht in der Gruppe Random-DE
Hallo! Als Sie Multithreading auf CodeGym studiert haben, sind Sie häufig auf die Konzepte „Mutex“ und „Monitor“ gestoßen. Können Sie, ohne einen Blick darauf zu werfen, sagen, worin sie sich unterscheiden? :) Wenn ja, gut gemacht! Wenn nicht (das kommt am häufigsten vor), ist das keine Überraschung. „Mutex“ und „Monitor“ sind eigentlich verwandte Konzepte. Wenn Sie außerdem Lektionen lesen und Videos zum Thema Multithreading auf anderen Websites ansehen, werden Sie auf ein weiteres ähnliches Konzept stoßen: „Semaphore“. Es hat auch eine sehr ähnliche Funktion wie Monitore und Mutexe. Deshalb werden wir diese drei Begriffe untersuchen. Wir werden uns einige Beispiele ansehen und zu einem endgültigen Verständnis dafür gelangen, wie sich diese Konzepte voneinander unterscheiden :)

Mutex

Ein Mutex (oder Lock) ist ein spezieller Mechanismus zum Synchronisieren von Threads. Eines ist jedem Objekt in Java „angehängt“ – das wissen Sie bereits :) Es spielt keine Rolle, ob Sie Standardklassen verwenden oder eigene Klassen erstellen, z. B. Cat und Dog : Alle Objekte aller Klassen haben einen Mutex . Der Begriff „Mutex“ kommt von „MUTual EXclusion“, was seinen Zweck perfekt beschreibt. Wie wir in einer unserer vorherigen Lektionen gesagt haben, ermöglicht ein Mutex sicherzustellen, dass jeweils nur ein Thread Zugriff auf das Objekt hat. Ein beliebtes Beispiel für einen Mutex aus der Praxis sind Toiletten. Wenn eine Person eine Toilettenabtrennung betritt, verriegelt sie die Tür von innen. Die Toilette ist wie ein Objekt, auf das mehrere Threads zugreifen können. Das Schloss an der Trenntür ist wie ein Mutex, und die Menschenreihe draußen stellt Fäden dar. Das Schloss an der Tür ist der Mutex der Toilette: Es sorgt dafür, dass nur eine Person hineinkommt. Was ist der Unterschied zwischen einem Mutex, einem Monitor und einem Semaphor?  - 2Mit anderen Worten: Es kann jeweils nur ein Thread mit gemeinsam genutzten Ressourcen arbeiten. Versuche anderer Threads (Personen), Zugriff auf belegte Ressourcen zu erhalten, schlagen fehl. Ein Mutex verfügt über mehrere wichtige Funktionen. Erstens sind nur zwei Zustände möglich: „entsperrt“ und „gesperrt“. Dies hilft uns zu verstehen, wie es funktioniert: Sie können Parallelen zu booleschen Variablen (wahr/falsch) oder Binärzahlen (0/1) ziehen. , der Staat kann nicht direkt kontrolliert werden. Java verfügt über keinen Mechanismus, der es Ihnen ermöglicht, explizit ein Objekt zu nehmen, seinen Mutex abzurufen und den gewünschten Status zuzuweisen. Mit anderen Worten, Sie können so etwas nicht tun:

Object myObject = new Object();
Mutex mutex = myObject.getMutex();
mutex.free();
Das bedeutet, dass Sie den Mutex eines Objekts nicht freigeben können. Nur die Java-Maschine hat direkten Zugriff darauf. Programmierer arbeiten mit Mutexes mithilfe der Werkzeuge der Sprache.

Monitor

Ein Monitor ist ein zusätzlicher „Überbau“ über einem Mutex. Tatsächlich ist ein Monitor ein Codeblock, der für den Programmierer „unsichtbar“ ist. Als wir zuvor über Mutexe gesprochen haben, haben wir ein einfaches Beispiel gegeben:

public class Main {

   private Object obj = new Object();

   public void doSomething() {

       // ...some logic, available for all threads

       synchronized (obj) {

           // Logic available to just one thread at a time
       }
   }
}
In dem mit dem synchronisierten Schlüsselwort markierten Codeblock wird der Mutex unseres obj- Objekts erfasst. Toll, wir können das Schloss besorgen, aber wie genau wird der „Schutz“ gewährleistet? Wenn wir das Wort synchronisiert sehen , was hindert die anderen Threads daran, den Block zu betreten? Der Schutz kommt von einem Monitor! Der Compiler wandelt das synchronisierte Schlüsselwort in mehrere spezielle Codeteile um. Kehren wir noch einmal zu unserem Beispiel mit der Methode doSomething() zurück . Wir ergänzen:

public class Main {

   private Object obj = new Object();

   public void doSomething() {

       // ...some logic, available for all threads

       // Logic available to just one thread at a time
       synchronized (obj) {

           /* Do important work that requires that the object
           be accessed by only one thread */
           obj.someImportantMethod();
       }
   }
}
Folgendes passiert „unter der Haube“, nachdem der Compiler diesen Code konvertiert hat:

public class Main {

   private Object obj = new Object();

   public void doSomething() throws InterruptedException {

       // ...some logic, available for all threads

       // Logic available to just one thread at a time:
     
       /* as long as the object's mutex is busy,
       all the other threads (except the one that acquired it) are put to sleep */
       while (obj.getMutex().isBusy()) {
           Thread.sleep(1);
       }

       // Mark the object's mutex as busy
       obj.getMutex().isBusy() = true;

       /* Do important work that requires that the object
       be accessed by only one thread */
       obj.someImportantMethod();

       // Free the object's mutex
       obj.getMutex().isBusy() = false;
   }
}
Natürlich handelt es sich hierbei nicht um ein reales Beispiel. Hier haben wir Java-ähnlichen Code verwendet, um darzustellen, was in der Java-Maschine passiert. Allerdings vermittelt dieser Pseudocode ein hervorragendes Verständnis dafür, was tatsächlich mit dem Objekt und den Threads innerhalb des synchronisierten Blocks passiert und wie der Compiler dieses Schlüsselwort in mehrere Anweisungen umwandelt, die für den Programmierer „unsichtbar“ sind. Grundsätzlich verwendet Java das synchronisierte Schlüsselwort, um einen Monitor darzustellen . Der gesamte Code, der im letzten Beispiel anstelle des synchronisierten Schlüsselworts erscheint, ist der Monitor.

Semaphor

Ein anderes Wort, das Ihnen bei Ihrem persönlichen Studium des Multithreadings begegnen wird, ist „Semaphor“. Lassen Sie uns herausfinden, was das ist und wie es sich von einem Monitor und einem Mutex unterscheidet. Ein Semaphor ist ein Werkzeug zum Synchronisieren des Zugriffs auf eine Ressource. Seine Besonderheit besteht darin, dass es einen Zähler verwendet, um den Synchronisationsmechanismus zu erstellen. Der Zähler sagt uns, wie viele Threads gleichzeitig auf die gemeinsam genutzte Ressource zugreifen können. Was ist der Unterschied zwischen einem Mutex, einem Monitor und einem Semaphor?  - 3Semaphore in Java werden durch die Semaphore -Klasse repräsentiert. Beim Erstellen von Semaphorobjekten können wir die folgenden Konstruktoren verwenden:

Semaphore(int permits)
Semaphore(int permits, boolean fair)
Wir übergeben dem Konstruktor Folgendes:
    int erlaubt – der Anfangs- und Maximalwert des Zählers. Mit anderen Worten: Dieser Parameter bestimmt, wie viele Threads gleichzeitig auf die gemeinsam genutzte Ressource zugreifen können.
  • boolean fair – legt die Reihenfolge fest, in der Threads Zugriff erhalten. Wenn fair wahr ist, wird den wartenden Threads der Zugriff in der Reihenfolge gewährt, in der sie ihn angefordert haben. Wenn es falsch ist, wird die Reihenfolge vom Thread-Scheduler bestimmt.
Ein klassisches Beispiel für die Verwendung von Semaphoren ist das Dining-Philosophen-Problem. Was ist der Unterschied zwischen einem Mutex, einem Monitor und einem Semaphor?  - 4Um das Verständnis zu erleichtern, vereinfachen wir es ein wenig. Stellen Sie sich vor, wir haben 5 Philosophen, die zu Mittag essen müssen. Zusätzlich haben wir einen Tisch, an dem maximal zwei Personen gleichzeitig Platz finden. Unsere Aufgabe ist es, alle Philosophen zu ernähren. Keiner von ihnen sollte hungern, und keiner von ihnen sollte sich gegenseitig „blockieren“, wenn er versucht, sich an den Tisch zu setzen (wir müssen einen Stillstand vermeiden). So wird unser Philosophenkurs aussehen:

class Philosopher extends Thread {

   private Semaphore sem;

   // Did the philosopher eat?
   private boolean full = false;

   private String name;

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

   public void run()
   {
       try
       {
           // If the philosopher has not eaten
           if (!full) {
               // Ask the semaphore for permission to run
               sem.acquire();
               System.out.println(name + " takes a seat at the table");

               // The philosopher eats
               sleep(300);
               full = true;

               System.out.println(name + " has eaten! He leaves the table");
               sem.release();

               // The philosopher leaves, making room for others
               sleep(300);
           }
       }
       catch(InterruptedException e) {
           System.out.println("Something went wrong!");
       }
   }
}
Und hier ist der Code zum Ausführen unseres Programms:

public class Main {

   public static void main(String[] args) {

       Semaphore sem = new Semaphore(2);
       new Philosopher(sem, "Socrates").start();
       new Philosopher(sem,"Plato").start();
       new Philosopher(sem,"Aristotle").start();
       new Philosopher(sem, "Thales").start();
       new Philosopher(sem, "Pythagoras").start();
   }
}
Wir haben ein Semaphor erstellt, dessen Zähler auf 2 gesetzt ist, um die Bedingung zu erfüllen: Nur zwei Philosophen können gleichzeitig essen. Das heißt, es können nur zwei Threads gleichzeitig ausgeführt werden, da unsere Philosopher- Klasse Thread erbt ! Die Methoden „acquire()“ und „release()“ der Semaphore- Klasse steuern ihren Zugriffszähler. Die Methode „acquire()“ fragt den Semaphor nach Zugriff auf die Ressource. Wenn der Zähler >0 ist, wird der Zugriff gewährt und der Zähler wird um 1 reduziert. Die release()Die Methode „gibt“ den zuvor gewährten Zugriff frei und gibt ihn an den Zähler zurück (erhöht den Zugriffszähler des Semaphors um 1). Was bekommen wir, wenn wir das Programm ausführen? Ist das Problem gelöst? Werden unsere Philosophen nicht kämpfen, während sie darauf warten, dass sie an die Reihe kommen? :) Hier ist die Konsolenausgabe, die wir erhalten haben:

Socrates takes a seat at the table 
Plato takes a seat at the table 
Socrates has eaten! He leaves the table 
Plato has eaten! He leaves the table 
Aristotle takes a seat at the table 
Pythagoras takes a seat at the table 
Aristotle has eaten! He leaves the table 
Pythagoras has eaten! He leaves the table 
Thales takes a seat at the table 
Thales has eaten! He leaves the table 
Wir haben es geschafft! Und obwohl Thales alleine speisen musste, glaube ich nicht, dass wir ihn beleidigt haben :) Möglicherweise sind Ihnen einige Ähnlichkeiten zwischen einem Mutex und einem Semaphor aufgefallen. Tatsächlich haben sie die gleiche Mission: den Zugriff auf eine Ressource zu synchronisieren. Was ist der Unterschied zwischen einem Mutex, einem Monitor und einem Semaphor?  - 5Der einzige Unterschied besteht darin, dass der Mutex eines Objekts jeweils nur von einem Thread erfasst werden kann, während im Fall eines Semaphors, der einen Thread-Zähler verwendet, mehrere Threads gleichzeitig auf die Ressource zugreifen können. Das ist kein Zufall :) Ein Mutex ist eigentlich ein Semaphormit einer Anzahl von 1. Mit anderen Worten, es ist ein Semaphor, der einen einzelnen Thread aufnehmen kann. Es wird auch als „binäres Semaphor“ bezeichnet, da sein Zähler nur zwei Werte haben kann – 1 („entsperrt“) und 0 („gesperrt“). Das ist es! Wie Sie sehen, ist es doch gar nicht so verwirrend :) Wenn Sie sich nun im Internet ausführlicher mit Multithreading befassen möchten, wird es für Sie etwas einfacher sein, sich in diesen Konzepten zurechtzufinden. Wir sehen uns in den nächsten Lektionen!
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION