CodeGym /Java-Blog /Random-DE /Thread-Synchronisation. Der synchronisierte Operator
Autor
Jesse Haniel
Lead Software Architect at Tribunal de Justiça da Paraíba

Thread-Synchronisation. Der synchronisierte Operator

Veröffentlicht in der Gruppe Random-DE
Hallo! Heute werden wir uns weiterhin mit den Funktionen der Multithread-Programmierung befassen und über die Thread-Synchronisierung sprechen. Thread-Synchronisation.  Der synchronisierte Operator - 1

Was ist Synchronisation in Java?

Außerhalb des Programmierbereichs handelt es sich dabei um eine Anordnung, die die Zusammenarbeit zweier Geräte oder Programme ermöglicht. Beispielsweise können ein Smartphone und ein Computer mit einem Google-Konto synchronisiert werden, und ein Website-Konto kann mit Konten in sozialen Netzwerken synchronisiert werden, damit Sie sich über diese anmelden können. Die Thread-Synchronisierung hat eine ähnliche Bedeutung: Es handelt sich um eine Anordnung, mit der Threads interagieren gegenseitig. In den vorherigen Lektionen lebten und arbeiteten unsere Threads getrennt voneinander. Einer führte eine Berechnung durch, ein zweiter schlief und ein dritter zeigte etwas auf der Konsole an, aber sie interagierten nicht. In echten Programmen sind solche Situationen selten. Mehrere Threads können aktiv mit demselben Datensatz arbeiten und ihn ändern. Dadurch entstehen Probleme. Stellen Sie sich vor, dass mehrere Threads Text an dieselbe Stelle schreiben, beispielsweise in eine Textdatei oder die Konsole. In diesem Fall wird die Datei oder Konsole zu einer gemeinsam genutzten Ressource. Die Threads sind sich der gegenseitigen Existenz nicht bewusst und schreiben daher einfach alles, was sie können, in der ihnen vom Thread-Scheduler zugewiesenen Zeit. In einer aktuellen Lektion haben wir ein Beispiel dafür gesehen, wohin das führt. Erinnern wir uns jetzt daran: Thread-Synchronisation.  Der synchronisierte Operator - 2Der Grund liegt darin, dass die Threads mit einer gemeinsamen Ressource (der Konsole) arbeiten, ohne ihre Aktionen untereinander zu koordinieren. Wenn der Thread-Scheduler Thread-1 Zeit zuweist, schreibt er sofort alles in die Konsole. Was andere Threads bereits geschrieben haben oder nicht, spielt keine Rolle. Das Ergebnis ist, wie Sie sehen, deprimierend. Aus diesem Grund haben sie ein spezielles Konzept, den Mutex (gegenseitigen Ausschluss) , in die Multithread-Programmierung eingeführt. Der Zweck eines Mutexbesteht darin, einen Mechanismus bereitzustellen, damit zu einem bestimmten Zeitpunkt nur ein Thread Zugriff auf ein Objekt hat. Wenn Thread-1 den Mutex von Objekt A erwirbt, können die anderen Threads nicht auf das Objekt zugreifen und es ändern. Die anderen Threads müssen warten, bis der Mutex von Objekt A freigegeben wird. Hier ein Beispiel aus dem Leben: Stellen Sie sich vor, Sie und 10 andere Fremde nehmen an einer Übung teil. Abwechselnd müssen Sie Ihre Ideen äußern und etwas besprechen. Aber weil man sich zum ersten Mal sieht, benutzt man einen „sprechenden Ball“, um sich nicht ständig zu unterbrechen und in Wut zu geraten: Nur die Person mit dem Ball kann sprechen. Auf diese Weise führen Sie am Ende eine gute und fruchtbare Diskussion. Im Wesentlichen ist der Ball ein Mutex. Wenn sich der Mutex eines Objekts in den Händen eines Threads befindet, können andere Threads nicht mit dem Objekt arbeiten.ObjectKlasse, was bedeutet, dass jedes Objekt in Java eine hat.

So funktioniert der synchronisierte Operator

Lernen wir ein neues Schlüsselwort kennen: synchronisiert . Es wird verwendet, um einen bestimmten Codeblock zu markieren. Wenn ein Codeblock mit dem synchronizedSchlüsselwort markiert ist, kann dieser Block jeweils nur von einem Thread ausgeführt werden. Die Synchronisierung kann auf unterschiedliche Weise implementiert werden. Beispielsweise durch die Deklaration einer gesamten Methode zur Synchronisierung:

public synchronized void doSomething() {

   // ...Method logic
}
Oder schreiben Sie einen Codeblock, in dem die Synchronisierung mithilfe eines Objekts durchgeführt wird:

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
       }
   }
}
Die Bedeutung ist einfach. Wenn ein Thread in den mit dem Schlüsselwort markierten Codeblock eindringt synchronized, erfasst er sofort den Mutex des Objekts und alle anderen Threads, die versuchen, denselben Block oder dieselbe Methode zu betreten, müssen warten, bis der vorherige Thread seine Arbeit abgeschlossen hat und den Monitor freigibt. Thread-Synchronisation.  Der synchronisierte Operator - 3Übrigens! Während des Kurses haben Sie bereits Beispiele gesehen synchronized, die jedoch anders aussahen:

public void swap()
{
   synchronized (this)
   {
       // ...Method logic
   }
}
Das Thema ist neu für Sie. Und natürlich wird es Verwirrung mit der Syntax geben. Merken Sie es sich also sofort, um später nicht durch die unterschiedlichen Schreibweisen verwirrt zu werden. Diese beiden Schreibweisen bedeuten dasselbe:

public void swap() {

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


public synchronized void swap() {

   }
}
Im ersten Fall erstellen Sie unmittelbar nach Eingabe der Methode einen synchronisierten Codeblock. Es wird vom thisObjekt synchronisiert, also vom aktuellen Objekt. Und im zweiten Beispiel wenden Sie das synchronizedSchlüsselwort auf die gesamte Methode an. Dadurch ist es nicht erforderlich, das für die Synchronisierung verwendete Objekt explizit anzugeben. Da die gesamte Methode mit dem Schlüsselwort gekennzeichnet ist, wird die Methode automatisch für alle Instanzen der Klasse synchronisiert. Wir werden uns nicht auf eine Diskussion darüber einlassen, welcher Weg besser ist. Wählen Sie zunächst die Methode, die Ihnen am besten gefällt :) Das Wichtigste ist, sich daran zu erinnern: Sie können eine Methode nur dann als synchronisiert deklarieren, wenn ihre gesamte Logik jeweils von einem Thread ausgeführt wird. Es wäre beispielsweise ein Fehler, die folgende doSomething()Methode zu synchronisieren:

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
       }
   }
}
Wie Sie sehen, enthält ein Teil der Methode Logik, die keine Synchronisierung erfordert. Dieser Code kann von mehreren Threads gleichzeitig ausgeführt werden und alle kritischen Stellen werden in einem separaten synchronizedBlock getrennt. Und noch etwas. Schauen wir uns unser Beispiel aus der Lektion mit dem Namenstausch genauer an:

public void swap()
{
   synchronized (this)
   {
       // ...Method logic
   }
}
Hinweis: Die Synchronisierung erfolgt mitthis. Das heißt, ein bestimmtesMyClassObjekt verwenden. Angenommen, wir haben 2 Threads (Thread-1undThread-2) und nur einMyClass myClassObjekt. Wenn in diesem FallThread-1die Methode aufgerufen wirdMethodemyClass.swap()aufzurufen,bleibt sie hängen, während sie auf die Freigabe des Mutex wartet. Wenn wir 2 Threads und 2 Objekte (undhaben, können unsere Threads die synchronisierten Methoden problemlos gleichzeitig auf verschiedenen Objekten ausführen. Der erste Thread führt Folgendes aus: myClass.swap()Thread-2MyClassmyClass1myClass2

myClass1.swap();
Der zweite führt Folgendes aus:

myClass2.swap();
In diesem Fall hat das synchronizedSchlüsselwort innerhalb der swap()Methode keinen Einfluss auf den Betrieb des Programms, da die Synchronisierung über ein bestimmtes Objekt erfolgt. Und im letzteren Fall haben wir zwei Objekte. Somit bereiten die Threads einander keine Probleme. Schließlich haben zwei Objekte zwei unterschiedliche Mutexe, und der Erwerb des einen ist unabhängig vom Erwerb des anderen .

Besonderheiten der Synchronisation in statischen Methoden

Was aber, wenn Sie eine statische Methode synchronisieren müssen ?

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

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

}
Es ist nicht klar, welche Rolle der Mutex hier spielen wird. Schließlich haben wir bereits festgestellt, dass jedes Objekt einen Mutex hat. Das Problem ist jedoch, dass wir keine Objekte benötigen, um die Methode aufzurufen MyClass.swap(): Die Methode ist statisch! Was kommt als nächstes? :/ Hier gibt es eigentlich kein Problem. Die Entwickler von Java haben sich um alles gekümmert :) Wenn eine Methode, die kritische nebenläufige Logik enthält, statisch ist, wird die Synchronisierung auf Klassenebene durchgeführt. Zur besseren Übersichtlichkeit können wir den obigen Code wie folgt umschreiben:

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;
       }
   }

}
Im Prinzip hätte man sich das auch selbst ausdenken können: Da es keine Objekte gibt, muss der Synchronisationsmechanismus irgendwie in die Klasse selbst integriert werden. Und so ist es: Wir können Klassen zum Synchronisieren verwenden.
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION