Hallo! Beim Studium von Multithreading in JavaRush sind Sie häufig auf die Konzepte „Mutex“ und „Monitor“ gestoßen. Können Sie jetzt, ohne einen Blick darauf zu werfen, antworten, worin sie sich unterscheiden? :) Wenn du könntest, gut gemacht! Wenn nicht (und das kommt am häufigsten vor) – kein Wunder. Die Konzepte „Mutex“ und „Monitor“ hängen tatsächlich zusammen. Darüber hinaus werden Sie beim Lesen von Vorträgen und beim Ansehen von Videos zum Thema Multithreading auf externen Ressourcen im Internet auf ein weiteres ähnliches Konzept stoßen – „Semaphor“. Auch seine Funktionalität ähnelt weitgehend einem Monitor und einem Mutex. Lassen Sie uns daher diese drei Begriffe verstehen, uns einige Beispiele ansehen und schließlich in unseren Köpfen das Verständnis dafür organisieren, wie sie sich voneinander unterscheiden :)
Wir werden die Begriffe zum besseren Verständnis etwas vereinfachen. Stellen Sie sich vor, wir haben 5 Philosophen, die ein Mittagessen brauchen. Gleichzeitig haben wir einen Tisch, an dem nicht mehr als zwei Personen gleichzeitig sitzen dürfen. Unsere Aufgabe ist es, alle Philosophen zu ernähren. Keiner von ihnen sollte hungern, noch sollten sie sich gegenseitig „blockieren“, wenn sie versuchen, sich an den Tisch zu setzen (wir müssen einen Stillstand vermeiden). So wird unser Philosophenkurs aussehen:
Mutex
Ein Mutex ist ein spezielles Objekt zum Synchronisieren von Threads. Es ist an jedes Objekt in Java „angehängt“ – das wissen Sie bereits :) Es spielt keine Rolle, ob Sie Standardklassen verwenden oder Ihre eigenen Klassen erstellen, sagen wir,Cat
und Dog
: Alle Objekte aller Klassen haben einen Mutex . Der Name „Mutex“ kommt vom englischen „MUTual EXclusion“ – „gegenseitiger Ausschluss“ und spiegelt seinen Zweck perfekt wider. Wie wir in einer der vorherigen Vorlesungen sagten, besteht die Aufgabe eines Mutex darin, einen solchen Mechanismus bereitzustellen, sodass nur ein Thread zu einem bestimmten Zeitpunkt Zugriff auf ein Objekt hat . Eine beliebte Analogie für einen Mutex im wirklichen Leben ist das „Toilettenbeispiel“. Wenn eine Person die Toilette betritt, verriegelt sie die Tür von innen. Die Toilette fungiert als Objekt, auf das mehrere Threads zugreifen können. Das Schloss an der Toilettentür übernimmt die Rolle eines Mutex, und die Menschenschlange draußen übernimmt die Rolle eines Threads. Das Schloss an der Tür ist ein Toiletten-Mutex: Es stellt sicher, dass sich jeweils nur eine Person im Inneren aufhalten kann. Mit anderen Worten: Es kann immer nur ein Thread gleichzeitig an gemeinsam genutzten Ressourcen arbeiten. Versuche anderer Threads (Personen), auf belegte Ressourcen zuzugreifen, schlagen fehl. Ein Mutex verfügt über mehrere wichtige Funktionen. Erstens sind nur zwei Zustände möglich – „frei“ und „beschäftigt“. Dies erleichtert das Verständnis der Funktionsweise: Parallelen können mit booleschen Variablen wahr/falsch oder dem binären Zahlensystem 1/0 gezogen werden. Zweitens können Staaten nicht direkt kontrolliert werden. In Java gibt es keine Mechanismen, die es Ihnen ermöglichen würden, explizit ein Objekt zu nehmen, seinen Mutex abzurufen und ihm 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();
Daher kann der Mutex des Objekts nicht freigegeben werden. Nur die Java-Maschine hat direkten Zugriff darauf. Programmierer arbeiten mit Mutexes mithilfe von Sprachtools.
Monitor
Ein Monitor ist ein zusätzliches „Add-on“ zu einem Mutex. Tatsächlich ist der Monitor ein für den Programmierer „unsichtbarer“ Code . Als wir vorhin über den Mutex sprachen, gaben wir ein einfaches Beispiel:public class Main {
private Object obj = new Object();
public void doSomething() {
//...etwas Logik, das allen Threads zur Verfügung steht
synchronized (obj) {
//Logik, die jeweils nur einem Thread zur Verfügung steht
}
}
}
In dem Codeblock, der mit dem Wort markiert ist synchronized
, wird der Mutex unseres Objekts erfasst obj
. Okay, die Gefangennahme erfolgt, aber wie genau wird der „Verteidigungsmechanismus“ erreicht? synchronized
Warum können andere Threads nicht in den Block hineingehen, wenn sie ein Wort sehen ? Es ist der Monitor, der den Schutzmechanismus schafft! Der Compiler wandelt das Wort synchronized
in mehrere spezielle Codeteile um. Kehren wir noch einmal zu unserem Beispiel mit der Methode zurück doSomething()
und ergänzen es:
public class Main {
private Object obj = new Object();
public void doSomething() {
//...etwas Logik, das allen Threads zur Verfügung steht
//Logik, die jeweils nur einem Thread zur Verfügung steht
synchronized (obj) {
/*выполнить важную работу, при которой доступ к ein Objektу
должен быть только у одного потока*/
obj.someImportantMethod();
}
}
}
Folgendes passiert „unter der Haube“ unseres Programms, nachdem der Compiler diesen Code konvertiert hat:
public class Main {
private Object obj = new Object();
public void doSomething() throws InterruptedException {
//...etwas Logik, das allen Threads zur Verfügung steht
//логика, которая одновременно доступна только для одного потока:
/*до тех пор, пока мьютекс ein Objektа занят -
любой другой поток (кроме того, который его захватил), спит*/
while (obj.getMutex().isBusy()) {
Thread.sleep(1);
}
//пометить мьютекс ein Objektа Wie занятый
obj.getMutex().isBusy() = true;
/*выполнить важную работу, при которой доступ к ein Objektу
должен быть только у одного потока*/
obj.someImportantMethod();
//освободить мьютекс ein Objektа
obj.getMutex().isBusy() = false;
}
}
Das Beispiel ist natürlich nicht real. Hier haben wir versucht, mithilfe von Java-ähnlichem Code wiederzugeben, was in diesem Moment in der Java-Maschine geschieht. Dieser Pseudocode vermittelt jedoch ein gutes Verständnis dafür, was tatsächlich mit dem Objekt und den Threads innerhalb des Blocks passiert synchronized
und wie der Compiler dieses Wort in mehrere Befehle umwandelt, die für den Programmierer „unsichtbar“ sind. Monitor wird in Java im Wesentlichen durch das Wort ausgedrücktsynchronized
. synchronized
Der gesamte Code, der im letzten Beispiel anstelle des Wortes erschien, ist der Monitor.
Semaphor
Ein anderes Wort, auf das Sie stoßen, wenn Sie sich selbst mit Multithreading befassen, ist „Semaphor“. Lassen Sie uns herausfinden, was es ist und wie es sich von einem Monitor und einem Mutex unterscheidet. Ein Semaphor ist ein Mittel zum Synchronisieren des Zugriffs auf eine Ressource. Seine Besonderheit besteht darin, dass beim Erstellen eines Synchronisationsmechanismus ein Zähler verwendet wird. Der Zähler sagt uns, wie viele Threads gleichzeitig auf eine gemeinsam genutzte Ressource zugreifen können. Semaphore werden in Java durch die Klasse repräsentiertSemaphore
. Beim Erstellen von Semaphorobjekten können wir die folgenden Konstruktoren verwenden:
Semaphore(int permits)
Semaphore(int permits, boolean fair)
Wir übergeben dem Konstruktor:
-
int permits
— anfänglicher und maximaler Zählerwert. Das heißt, wie viele Threads können gleichzeitig auf eine gemeinsam genutzte Ressource zugreifen; -
boolean fair
- um die Reihenfolge festzulegen, in der Threads Zugriff erhalten. Wennfair
= true , wird den wartenden Threads der Zugriff in der Reihenfolge gewährt, in der sie ihn angefordert haben. Wenn es false ist , wird die Reihenfolge vom Thread-Scheduler bestimmt.
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 ("Was-то пошло не так!");
}
}
}
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,"Сократ").start();
new Philosopher(sem,"Платон").start();
new Philosopher(sem,"Аристотель").start();
new Philosopher(sem,"Фалес").start();
new Philosopher(sem,"Пифагор").start();
}
}
Wir haben ein Semaphor mit einer Zählung von 2 erstellt, um die Bedingung zu erfüllen, dass nur zwei Philosophen gleichzeitig essen können. Das heißt, es können nur zwei Threads gleichzeitig arbeiten, da unsere Klasse Philosopher
von geerbt wird Thread
! Die Klassen acquire()
und Methoden steuern ihren Berechtigungszähler. Die Methode fordert die Erlaubnis zum Zugriff auf eine Ressource vom Semaphor an. Wenn der Zähler > 0 ist, wird die Berechtigung erteilt und der Zähler um 1 verringert. Die Methode „gibt“ die zuvor erteilte Berechtigung frei und gibt sie an den Zähler zurück (wodurch der Erteilungszähler des Semaphors um 1 erhöht wird). Was erhalten wir, wenn wir das Programm ausführen? Ist das Problem gelöst? Werden unsere Philosophen kämpfen, während sie darauf warten, dass sie an die Reihe kommen? :) Dies ist die Konsolenausgabe, die wir erhalten haben: Sokrates setzt sich an den Tisch. Platon setzt sich an den Tisch, den Sokrates gegessen hat! Er verlässt den Tisch. Platon hat gegessen! Er verlässt den Tisch. Aristoteles setzt sich an den Tisch. Pythagoras setzt sich an den Tisch, den Aristoteles gegessen hat! Er verlässt den Tisch, den Pythagoras gegessen hat! Er verlässt den Tisch, Thales setzt sich an den Tisch, den Thales gegessen hat! Er verlässt den Tisch. Wir haben es geschafft! Und obwohl Thales alleine zu Abend essen musste, glaube ich, dass er nicht sauer auf uns ist :) Möglicherweise sind Ihnen einige Ähnlichkeiten zwischen einem Mutex und einem Semaphor aufgefallen. Im Allgemeinen haben sie denselben Zweck: den Zugriff auf eine Ressource zu synchronisieren. Der einzige Unterschied besteht darin, dass der Mutex eines Objekts jeweils nur von einem Thread erfasst werden kann, während im Fall eines Semaphors ein Thread-Zähler verwendet wird und mehrere von ihnen gleichzeitig auf die Ressource zugreifen können. Und das ist nicht nur eine zufällige Ähnlichkeit :) Tatsächlich ist ein Mutex ein Semaphor mit einer einzigen Stelle . Das heißt, es handelt sich um einen Semaphor, dessen Zähler zunächst auf 1 gesetzt ist. Er wird auch „binärer Semaphor“ genannt, da sein Zähler nur 2 Werte haben kann – 1 („frei“) und 0 („beschäftigt“). Das ist alles! Wie Sie sehen, war alles nicht so verwirrend :) Wenn Sie sich nun im Internet genauer mit dem Thema Multithreading befassen möchten, wird es Ihnen etwas leichter fallen, sich in den Konzepten zurechtzufinden. Wir sehen uns in den nächsten Lektionen! release()
Semaphore
acquire()
release()
GO TO FULL VERSION