JavaRush /Java-Blog /Random-DE /Thread-Synchronisation. Synchronisierter Operator in Java...

Thread-Synchronisation. Synchronisierter Operator in Java

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.  Operator synchronisiert - 1
Was ist „Synchronisation“? Außerhalb des Programmierbereichs bezieht sich dies auf eine Art Setup, das 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 persönliches Konto auf einer Website kann mit Konten in sozialen Netzwerken synchronisiert werden, um sich über diese anzumelden. Die Thread-Synchronisierung hat eine ähnliche Bedeutung: Sie legt fest, wie Threads miteinander interagieren. In den vorherigen Vorlesungen lebten und arbeiteten unsere Threads getrennt voneinander. Einer zählte etwas, der zweite schlief, der dritte zeigte etwas auf der Konsole an, aber sie interagierten nicht miteinander. In echten Programmen sind solche Situationen selten. Mehrere Threads können beispielsweise aktiv mit demselben Datensatz arbeiten und darin etwas verändern. Dadurch entstehen Probleme. Stellen Sie sich vor, dass mehrere Threads Text an denselben Ort schreiben, beispielsweise in eine Textdatei oder in die Konsole. Diese Datei oder Konsole wird in diesem Fall zu einer gemeinsam genutzten Ressource. Threads wissen nichts von der Existenz des anderen Threads und schreiben daher einfach alles auf, was sie in der ihnen vom Thread-Scheduler zugewiesenen Zeit bewältigen können. Wir hatten kürzlich in einer Vorlesung des Kurses ein Beispiel dafür, wozu das führen würde, erinnern wir uns daran: Thread-Synchronisation.  Synchronisierter Operator - 2Der Grund liegt darin, dass die Threads mit einer gemeinsamen Ressource, der Konsole, arbeiteten, ohne Aktionen untereinander abzustimmen. Wenn der Thread-Scheduler Thread-1 Zeit zugewiesen hat, schreibt er sofort alles in die Konsole. Was andere Threads bereits geschrieben haben oder nicht schreiben konnten, ist nicht wichtig. Das Ergebnis ist, wie Sie sehen, katastrophal. Daher wurde in der Multithread-Programmierung ein spezielles Mutex-Konzept eingeführt (vom englischen „Mutex“, „gegenseitiger Ausschluss“ – „gegenseitiger Ausschluss“) . Der Zweck eines Mutex besteht darin, einen Mechanismus bereitzustellen, damit nur ein Thread zu einem bestimmten Zeitpunkt Zugriff auf ein Objekt hat. Wenn Thread-1 den Mutex von Objekt A erworben hat, haben andere Threads keinen Zugriff darauf, um etwas daran zu ändern. Bis der Mutex von Objekt A freigegeben wird, müssen die verbleibenden Threads warten. Beispiel aus dem wirklichen Leben: Stellen Sie sich vor, dass Sie und 10 andere Fremde an einer Schulung teilnehmen. Sie müssen abwechselnd Ideen äußern und etwas besprechen. Aber da man sich zum ersten Mal sieht, um sich nicht ständig gegenseitig zu unterbrechen und nicht in Aufruhr zu verfallen, gilt die „Talking-Ball“-Regel: Nur einer kann sprechen – derjenige, der den Ball hat Seine Hände. Auf diese Weise wird die Diskussion angemessen und fruchtbar. Ein Mutex ist also im Wesentlichen ein solcher Ball. Wenn sich der Mutex eines Objekts in den Händen eines Threads befindet, können andere Threads nicht auf das Objekt zugreifen. Sie müssen nichts tun, um einen Mutex zu erstellen: Er ist bereits in die Klasse integriert Object, was bedeutet, dass jedes Objekt in Java über ihn verfügt.

So funktioniert der synchronisierte Operator in Java

Machen wir uns mit einem neuen Schlüsselwort vertraut – synchronisiert . Es markiert einen bestimmten Teil unseres Codes. Wenn ein Codeblock mit dem synchronisierten Schlüsselwort gekennzeichnet ist, bedeutet dies, dass der Block jeweils nur von einem Thread ausgeführt werden kann. Die Synchronisierung kann auf unterschiedliche Weise implementiert werden. Erstellen Sie beispielsweise eine vollständige synchronisierte Methode:
public synchronized void doSomething() {

   //...Methodenlogik
}
Oder schreiben Sie einen Codeblock, in dem die Synchronisierung für ein Objekt durchgeführt wird:
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
       }
   }
}
Die Bedeutung ist einfach. Wenn ein Thread einen Codeblock betritt, der mit dem Wort synchronisiert gekennzeichnet ist, erhält er sofort den Mutex des Objekts, und alle anderen Threads, die versuchen, denselben Block oder dieselbe Methode einzugeben, müssen warten, bis der vorherige Thread seine Arbeit abgeschlossen und den Mutex freigegeben hat Monitor. Thread-Synchronisation.  Synchronisierter Operator - 3Übrigens! In den Vorlesungen des Kurses haben Sie bereits Beispiele für synchronisiert gesehen, die jedoch anders aussahen:
public void swap()
{
   synchronized (this)
   {
       //...Methodenlogik
   }
}
Das Thema ist für Sie neu und natürlich wird es zunächst zu Verwirrung mit der Syntax kommen. Denken Sie deshalb gleich daran, um später bei den Schreibmethoden nicht verwirrt zu werden. Diese beiden Schreibmethoden bedeuten dasselbe:
public void swap() {

   synchronized (this)
   {
       //...Methodenlogik
   }
}


public synchronized void swap() {

   }
}
Im ersten Fall erstellen Sie unmittelbar nach Eingabe der Methode einen synchronisierten Codeblock. Die Synchronisierung erfolgt nach Objekt this, also nach dem aktuellen Objekt. Und im zweiten Beispiel haben Sie das Wort synchronisiert in die gesamte Methode eingefügt. Es ist nicht mehr notwendig, explizit anzugeben, auf welchem ​​Objekt die Synchronisierung durchgeführt wird. Sobald eine gesamte Methode mit einem Wort markiert ist, wird diese Methode automatisch für alle Objekte der Klasse synchronisiert. Lassen Sie uns nicht auf die Diskussion eingehen, welche Methode besser ist. Wählen Sie zunächst aus, was Ihnen am besten gefällt :) Das Wichtigste ist, sich daran zu erinnern: Sie können eine Methode nur dann als synchronisiert deklarieren, wenn die gesamte darin enthaltene Logik von einem Thread gleichzeitig ausgeführt wird. doSomething()In diesem Fall wäre es beispielsweise ein Fehler, die Methode zu synchronisieren:
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
       }
   }
}
Wie Sie sehen, enthält ein Teil der Methode Logik, für die keine Synchronisierung erforderlich ist. Der darin enthaltene Code kann von mehreren Threads gleichzeitig ausgeführt werden und alle kritischen Stellen werden einem separaten synchronisierten Block zugeordnet. Und einen Moment. Schauen wir uns unser Beispiel aus der Vorlesung mit dem Namensaustausch unter die Lupe:
public void swap()
{
   synchronized (this)
   {
       //...Methodenlogik
   }
}
Bitte beachten Sie: Die Synchronisierung erfolgt über this. Das heißt, für ein bestimmtes Objekt MyClass. Stellen Sie sich vor, wir haben zwei Threads ( Thread-1und Thread-2) und nur ein Objekt MyClass myClass. Wenn in diesem Fall Thread-1die Methode aufgerufen wird myClass.swap(), ist der Mutex des Objekts beschäftigt, und Thread-2wenn Sie versuchen, ihn aufzurufen, myClass.swap()bleibt er hängen und wartet darauf, dass der Mutex frei wird. Wenn wir 2 Threads und 2 Objekte haben MyClassmyClass1und zwar myClass2auf unterschiedlichen Objekten – können unsere Threads problemlos gleichzeitig synchronisierte Methoden ausführen. Der erste Thread macht:
myClass1.swap();
Der zweite macht:
myClass2.swap();
In diesem Fall hat das synchronisierte Schlüsselwort innerhalb der Methode swap()keinen Einfluss auf den Betrieb des Programms, da die Synchronisierung für ein bestimmtes Objekt ausgeführt wird. Und im letzteren Fall haben wir zwei Objekte. Daher bereiten die Threads einander keine Probleme. Schließlich haben zwei Objekte zwei unterschiedliche Mutexe und ihre Erfassung hängt nicht voneinander ab.

Merkmale der Synchronisation in statischen Methoden

Was aber, wenn Sie eine statische Methode synchronisieren müssen?
class MyClass {
   private static String name1 = „Olja“;
   private static String name2 = „Lena“;

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

}
Es ist nicht klar, was in diesem Fall als Mutex dienen wird. Schließlich haben wir bereits entschieden, dass jedes Objekt einen Mutex hat. Das Problem ist jedoch, dass wir zum Aufrufen einer statischen Methode MyClass.swap()keine Objekte benötigen: Die Methode ist statisch! Und was dann? :/ Eigentlich gibt es damit kein Problem. Die Entwickler von Java haben sich um alles gekümmert :) Wenn die Methode, die die kritische „Multithread-Logik“ enthält, statisch ist, erfolgt die Synchronisierung klassenweise. Zur besseren Übersichtlichkeit kann der obige Code wie folgt umgeschrieben werden:
class MyClass {
   private static String name1 = „Olja“;
   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 den Klassen selbst „fest verdrahtet“ sein. So ist es: Sie können auch klassenübergreifend synchronisieren.
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION