Vor Java 5 wurde das synchronisierte Schlüsselwort verwendet, um die Sperrfunktion zu implementieren.
Das synchronisierte Schlüsselwort kann als Modifikator (synchronisierte Methode) oder als Anweisung innerhalb einer Funktion (synchronisierter Codeblock) verwendet werden.
Um synchron zu beherrschen, besteht der Schlüssel darin, die Verwendung dieses Ding als Schloss zu beherrschen. Für nicht statische Methoden (Mitgliedsmethoden) einer Klasse bedeutet es, die Sperre der Objektinstanz zu erhalten. Für statische Methoden (Klassenmethoden) einer Klasse ist es notwendig, die Sperre des Klassenobjekts zu erhalten. Für synchrone Codeblöcke müssen angegeben werden, welcher Objektschloss erhalten wird. Synchronisierte nicht-statische Methoden können als synchronisiertes (this) {…} Codeblock angesehen werden, der die gesamte Methode enthält.
Unabhängig davon, ob es sich um einen Synchroncodeblock oder eine Synchronisationsmethode handelt, kann nur ein Thread gleichzeitig eingegeben werden (höchstens ein Thread wird gleichzeitig das Codesegment ausführen), und wenn andere Threads versuchen, einzugeben (ob es sich um denselben Synchronblock oder einen anderen Synchronisationsblock handelt), hängt JVM auf (in den Wartungsschlossbad). Diese Struktur wird als kritischer Abschnitt in der Parallelitätstheorie bezeichnet.
In JVM wird jeder Thread gleichzeitig eine Cache -Kopie der von ihm verarbeitenden Daten haben, um die Effizienz zu verbessern. Wenn wir Synchronzed für die Synchronisation verwenden, ist das, was wirklich synchronisiert ist, der Speicherblock, der das gesperrte Objekt in verschiedenen Threads darstellt (die Kopierdaten bleiben mit dem Hauptspeicher synchronisiert. Jetzt wissen wir, warum die Wortsynchronisation verwendet wird). Einfach ausgedrückt, nachdem der Synchronisationsblock oder die Synchronisationsmethode ausgeführt wurde, müssen alle Änderungen am gesperrten Objekt vor dem Freigeben der Sperre in den Hauptspeicher zurückgeschrieben werden. Nach Eingabe des Synchronisationsblocks und der Erfassung der Sperre werden die Daten des gesperrten Objekts aus dem Hauptspeicher gelesen, und die Datenkopie des Threads, das die Sperre hält, muss mit der Datenansicht im Hauptspeicher synchronisiert werden.
Die folgenden Beispiele sind spezifische Beispiele, um die verschiedenen Synchronsituationen zu veranschaulichen.
Synchronisierte Synchronisationsmethode
Schauen wir uns zunächst das Beispiel der Synchronisationsmethode an:
öffentliche Klasse SynchronizedTest1 erweitert Thread {private synchronisierte void testSynchronizedMethod () {für (int i = 0; i <10; i ++) {System.out.println (Thread.CurrentThread (). try {thread.sleep (100); } catch (interruptedException e) {e.printstacktrace (); }}} @Override public void run () {TestsynchronizedMethod (); } public static void main (String [] args) {synchronisierte Test1 T = neuer synchronisierter Test1 (); t.start (); T.TestSynchronizedMethod (); }}Ausführen der Programmausgaben:
Haupt -TestsynchronizedMethod: 0 Haupt -TestsynchronizedMethod: 1 Haupt -TestsynchronizedMethod: 2 Haupt -TestsynchronizedMethod: 3 Haupt -TestsynchronizedMethod: 4 HauptstestsynchronizedMethod: 5 HaupttestsynchronizedMethod: 8 MainchronizedMonizedMethod: 7 MainchronizedMethod: 7 MainchronizedMethod: 7 MainchronizedMethod: 7 MainchronizedMethod: 7 MainchronizedMethod: 7 MainchronizedMethod: 7 MainchronizedMethod: 7 MainchronizedMethod: 7 MainchronizedMethod: 7 MainchronizedMethod. TestsynchronizedMethod: 9 Thread-0 TestsynchronizedMethod: 0 Thread-0 TestsynchronizedMethod: 1 Thread-0 TestsynchronizedMethod: 2 Thread-0 TestsynchronizedMethod: 3 Thread-0 TestsynchronizedMethod: 4 Thread-0-Thread-0-Thread-0-Thread-0-Thread-0-testynchronizedMethod0-0-thread-0-Test TestsynchronizedMethod: 7 Thread-0 TestsynchronizedMethod: 8 Thread-0 TestsynchronizedMethod: 9
Sie können sehen, dass die TestsynchronizedMethod -Methode synchron zwischen zwei Threads ausgeführt wird.
Wenn die Hauptmethode an Folgendes geändert wird, können die beiden Threads nicht synchron ausführen, da der Synchronisationsmonitor der beiden Threads nicht dasselbe Objekt ist und keine synchrone Rolle spielen kann.
public static void main (String [] args) {Thread t = neuer synchronisierter Test1 (); t.start (); Thread T1 = neuer synchronisierter Test1 (); t1.start (); }Das Ausgabeergebnis ist wie folgt:
Thread-0 testSynchronizedMethod:0 Thread-1 testSynchronizedMethod:0 Thread-0 testSynchronizedMethod:1 Thread-1 testSynchronizedMethod:1 Thread-0 testSynchronizedMethod:2 Thread-1 testSynchronizedMethod:2 Thread-0 testSynchronizedMethod:3 Thread-1 testSynchronizedMethod:3 Thread-0 TestsynchronizedMethod: 4 Thread-1 TestsynchronizedMethod: 4 Thread-0 TestsynchronizedMethod: 5 Thread-1 TestsynchronizedMethod: 5 Thread-0 TestsynchronizedMethod: 6 Thread-1 TestsynchronizedMethod: 6 Thread-0-Thread-0-0-Thread-0-0 TestsynchronizedMethod: 8 Thread-1 TestsynchronizedMethod: 8 Thread-0 TestsynchronizedMethod: 9 Thread-1 TestsynchronizedMethod: 9
Wenn die modifizierte Hauptmethode zwischen zwei Threads synchron ausgeführt werden kann, muss die TestsynchronizedMethod -Methode als statische Methode deklariert werden, sodass die Monitore der beiden Threads das gleiche Objekt (Klassenobjekt) sind und synchron ausgeführt werden können. Der geänderte Code sieht folgendermaßen aus:
öffentliche Klasse SynchronizedTest1 erweitert Thread {private statische synchronisierte void testSynchronizedMethod () {für (int i = 0; i <10; i ++) {System.out.println (Thread.currentThread (). try {thread.sleep (100); } catch (interruptedException e) {e.printstacktrace (); }}} @Override public void run () {TestsynchronizedMethod (); } public static void main (String [] args) {thread t = neu synchronisierte Test1 (); t.start (); Thread T1 = neuer synchronisierter Test1 (); t1.start (); }}Das Ausgabeergebnis ist wie folgt:
Thread-0 testSynchronizedMethod:0 Thread-0 testSynchronizedMethod:1 Thread-0 testSynchronizedMethod:2 Thread-0 testSynchronizedMethod:3 Thread-0 testSynchronizedMethod:4 Thread-0 testSynchronizedMethod:5 Thread-0 testSynchronizedMethod:6 Thread-0 testSynchronizedMethod:7 Thread-0 testSynchronizedMethod:8 Thread-0 testSynchronizedMethod:9 Thread-1 testSynchronizedMethod:0 Thread-1 testSynchronizedMethod:1 Thread-1 testSynchronizedMethod:2 Thread-1 testSynchronizedMethod:3 Thread-1 testSynchronizedMethod:4 Thread-1 testSynchronizedMethod:5 Thread-1 testSynchronizedMethod:6 Thread-1 TestsynchronizedMethod: 7 Thread-1 TestsynchronizedMethod: 8 Thread-1 TestsynchronizedMethod: 9
Die Situation von Synchronblöcken ähnelt der Synchronisationsmethode, mit der Ausnahme, dass der Synchronblock die Granularität der Synchronisationsregelung verringert, was die Effizienz der parallelen Multi-Thread-Ausführung besser ausüben kann.
Verwenden Sie dieses Objekt, um die Synchronisation zwischen denselben Objektinstanzen zu steuern:
öffentliche Klasse SynchronizedTest2 erweitert Thread {private void testsynchronizedBlock () {synchronisierte (this) {for (int i = 0; i <10; i ++) {system.out.println (Thread.current thread (). try {thread.sleep (100); } catch (interruptedException e) {e.printstacktrace (); }}}} @Override public void run () {testSynchronizedBlock (); } public static void main (String [] args) {synchronisierte test2 T = neu synchronisierte Test2 (); t.start (); T.TestSynchronizedBlock (); }}Ausgangsergebnis:
Haupt-Testsynchronizedblock: 0 Haupt-Testsynchronizedblock: 1 Haupt-Testsynchronizedblock: 2 Haupt-Testsynchronizedblock: 3 Haupt-Testsynchronizedblock: 4 Haupt-Testsynchronisierungsblock: 5 Haupt-Testsynchronisierungsblock: 6 Haupttestsblock: 7 Mainstests-TestsynchronizedBlock: 8 Haupttestsblock: 9 Main-Tests-Tests-Tests-Schock: 8 Haupttestsblock: 9 Main-testsnchronizedblock: 8 Haupttestsblock: 9 Hauptdastsblock: 8 Hauptdross-Symnizedblock: 8 Haupttestsblock: 9 Hauptdokumentierblöcke: 8 Hauptdokumentation: 8 Haupttrocknichdlock: 8 Haupttastblock-0-testschonizedblöcke: 8 Haupttestsblock: 8 Haupttestsblöcke, 9 Haupttrocknessblöcke: 8 Haupttastblöcke-0-0. TestsynchronizedBlock: 0 Thread-0 TestsynchronizedBlock: 1 Thread-0 TestsynchronizedBlock: 2 Thread-0 TestsynchronizedBlock: 3 Thread-0 TestsynchronizedBlock: 4 Thread-0 TestsynchronizedBlock: 5 Thread-0-Thread-0-Thread-0-Thread-0-Thread-0-Thread-0-Thread-0-Thread-0-Testsytrock: 7-Thread-0-Thread-0-Thread-0-Testsyl: 7-Thread-0-Thread-0-Thread-0-Testsymb. TestsynchronizedBlock: 9
Verwenden Sie Klassenobjekte, um die Synchronisation zwischen verschiedenen Instanzen zu steuern:
öffentliche Klasse SynchronizedTest2 erweitert Thread {private void testsynchronizedBlock () {synchronized (synchronisierte Test2.Class) {für (int i = 0; i <10; i ++) {System.out.println (Thread.Current thread (). try {thread.sleep (100); } catch (interruptedException e) {e.printstacktrace (); }}}} @Override public void run () {testSynchronizedBlock (); } public static void main (String [] args) {thread t = new SynchronizedTest2 (); t.start (); Thread T2 = neuer synchronisierter Test2 (); t2.Start (); }}Ausgangsergebnis:
Thread-0 TestsynchronizedBlock: 0 Thread-0 Testsynchronizedblock: 1 Thread-0 TestsynchronizedBlock: 2 Thread-0 TestsynchronizedBlock: 3 Thread-0 TestsynchronizedBlock: 4 Thread-0 TestsynchronizedBlock: 5 Thread-0-Thread-0-Thread-0-Thread-0-Thread-0-Thread-0-Thread-0-Testsytrock: 6 Thread-0-Thread-0. Thread-0 testSynchronizedBlock:9 Thread-1 testSynchronizedBlock:0 Thread-1 testSynchronizedBlock:1 Thread-1 testSynchronizedBlock:2 Thread-1 testSynchronizedBlock:3 Thread-1 testSynchronizedBlock:4 Thread-1 testSynchronizedBlock:5 Thread-1 testSynchronizedBlock:6 Thread-1 testSynchronizedBlock:7 Thread-1 TestsynchronizedBlock: 8 Thread-1 TestsynchronizedBlock: 9
Bei Verwendung des synchronisierten Schlüsselworts zur Synchronisationskontrolle müssen Sie den Objektmonitor erfassen. Nur der Prozess, der den Monitor erhält, kann ausgeführt werden, und alles andere muss gewartet werden, um den Monitor zu erhalten. Jedes Nicht-Null-Objekt kann als Objektmonitor verwendet werden. Bei synchronisierten Akten auf eine Methode ist die Objektinstanz gesperrt. Bei einer statischen Methode ist die Objektinstanz entsprechend dem Objekt gesperrt.
Synchronische Methode für zwei Threads, um gleichzeitig auf ein Objekt zuzugreifen
Wenn zwei gleichzeitige Threads auf die synchrone Methode desselben Objekts zugreifen, kann nur ein Thread ausgeführt werden. Ein weiterer Thread muss warten, bis der aktuelle Thread dies ausführt, bevor er ausgeführt werden kann.
public class twotHhread {public static void main (String [] args) {endgültig twothread twotHhread = new twotHhread (); Thread t1 = neuer Thread (new Runnable () {public void run () {twothread.syncMethod ();}}, "a"); Thread t2 = neuer Thread (neuer runnable () {public void run () {twothread.syncMethod ();}}, "b"); t1.start (); t2.Start (); } public synchronisierte void syncMethod () {für (int i = 0; i <5; i ++) {System.out.println (thread.currentThread (). getName () + ":" + i); try {thread.sleep (500); } catch (InterruptedException IE) {}}}}Ausgangsergebnis:
A: 0A: 1A: 2A: 3A: 4B: 0B: 1B: 2B: 3B: 4
Die Synchronisationsmethode zweier Objekte wird von zwei Threads zugegriffen
In diesem Fall funktioniert synchronisiert nicht wie die gewöhnliche Methode. Weil die entsprechenden Schlösser ihre jeweiligen Objekte sind.
public class zweiObject {public static void main (String [] args) {Final TwoObject Object1 = new TwoObject (); Thread t1 = neuer Thread (neuer Runnable () {public void run () {Object1.syncMethod ();}}, "Object1"); t1.start (); Final TwoObject Object2 = new TwoObject (); Thread t2 = neuer Thread (neuer runnable () {public void run () {public void run () {Object2.syncMethod ();}}, "Object2"); t2.Start (); } public synchronisierte void syncMethod () {für (int i = 0; i <5; i ++) {System.out.println (thread.currentThread (). getName () + ":" + i); try {thread.sleep (500); } catch (InterruptedException IE) {}}}}Einer der möglichen Ausgänge:
Object2: 0Object1: 0Object1: 1Object2: 1Object2: 2Object1: 2Object2: 3Object1: 3Object1: 4Object2: 4
Die beiden Threads greifen auf die synchronisierte statische Methode zu
In diesem Fall kann zu jeder Zeit nur ein Thread die statische Methode ausführen.
Zugriff auf synchrone Methoden und asynchrone Methoden gleichzeitig, wenn ein Thread auf eine Synchronisationsmethode eines Objekts zugreift, kann ein anderer Thread weiterhin auf die asynchronen Methoden in diesem Objekt zugreifen.
public class syncandnosync {public static void main (String [] args) {endgültig syncandnosync syncandnosync = new Syncandnosync (); Thread t1 = neuer Thread (new Runnable () {public void run () {syncandnosync.syncMethod ();}}, "a"); t1.start (); Thread t2 = neuer Thread (neuer Runnable () {public void run () {syncandnosync.nosyncMethod ();}}, "b"); t2.Start (); } public synchronisierte void syncMethod () {für (int i = 0; i <5; i ++) {System.out.println (Thread.CurrentThread (). getName () + "at syncMethod ():" + i); try {thread.sleep (500); } catch (InterruptedException IE) {}}} public void nosyncMethod () {für (int i = 0; i <5; i ++) {System.out.println (thread.currentThread (). try {thread.sleep (500); } catch (InterruptedException IE) {}}}}Eine mögliche Ausgabe:
B at noSyncMethod(): 0A at syncMethod(): 0B at noSyncMethod(): 1A at syncMethod(): 1B at noSyncMethod(): 2A at syncMethod(): 2B at noSyncMethod(): 3A at syncMethod(): 3A at syncMethod(): 4B at nosyncMethod (): 4
Verschiedene Synchronisationsmethoden zum Zugriff auf dasselbe Objekt
Wenn ein Thread auf die Synchronisationsmethode A eines Objekts zugreift, werden andere Threads auf alle anderen Synchronisationsmethoden im Objekt zugreifen. Da der erste Thread die Objektsperrung erhalten hat und andere Threads die Sperre nicht erhalten können, obwohl er auf eine andere Methode zugreift, wird die Sperre nicht erhalten und kann nicht darauf zugreifen.
public class TwosyncMethod {public static void main (String [] args) {endgültige twosyncMethod twosyncMethod = new TwosyncMethod (); Thread t1 = neuer Thread (new Runnable () {public void run () {twosyncMethod.syncMethod1 ();}}, "a"); t1.start (); Thread t2 = neuer Thread (neuer Runnable () {public void run () {twosyncMethod.syncMethod2 ();}}, "B"); t2.Start (); } public synchronisierte void syncMethodod1 () {für (int i = 0; i <5; i ++) {System.out.println (thread.currentThread (). getName () + "at syncMethod1 ():" + i); try {thread.sleep (500); } catch (InterruptedException IE) {}}} public synchronisierte void syncMethod2 () {für (int i = 0; i <5; i ++) {System.out.println (Thread.current thread (). try {thread.sleep (500); } catch (InterruptedException IE) {}}}}Ausgangsergebnis:
A at syncMethod1(): 0A at syncMethod1(): 1A at syncMethod1(): 2A at syncMethod1(): 3A at syncMethod1(): 4B at syncMethod2(): 0B at syncMethod2(): 1B at syncMethod2(): 2B at syncMethod2(): 3B at SyncMethod2 (): 4