1. grundlegende Verwendung von synchronisiert
Synchronisiert ist die am häufigsten verwendete Methode zur Lösung von Parallelitätsproblemen in Java und der einfachsten Methode. Synchronized hat drei Hauptfunktionen: (1) Gewährleisten Sie den Thread, der sich gegenseitig ausschließt, den Zugriffs -Synchronisationscode (2) sicher, dass die Änderung gemeinsamer Variablen zeitnahtbar sein kann (3) das Nachbestellungsproblem effektiv lösen kann. Synchronized hat drei Verwendungen von Synchronisierten:
(1) Gewöhnliche Methode zur Änderung
(2) statische Methoden ändern
(3) Ändern Sie den Codeblock
Als nächstes werde ich einige Beispielprogramme verwenden, um diese drei Verwendungsmethoden zu veranschaulichen (zum Vergleichszweck, mit Ausnahme der unterschiedlichen Verwendungsmethoden der Synchronisierung sind die anderen drei Codes im Grunde genommen konsistent).
1. Keine Synchronisation:
Code -Snippet 1:
Paket com.paddx.test.concurrent; öffentliche Klasse SynchronizedTest {public void method1 () {System.out.println ("Methode 1 start"); try {System.out.println ("Methode 1 Execute"); Thread.sleep (3000); } catch (interruptedException e) {e.printstacktrace (); } System.out.println ("Methode 1 End"); } public void method2 () {System.out.println ("Methode 2 start"); try {System.out.println ("Methode 2 execute"); Thread.sleep (1000); } catch (interruptedException e) {e.printstacktrace (); } System.out.println ("Methode 2 End"); } public static void main (String [] args) {endgültig synchronisierte Test test = new SynchronizedTest (); neuer Thread (new Runnable () {@Override public void run () {Test.Method1 ();}}). start (); neuer Thread (new Runnable () {@Override public void run () {Test.Method2 ();}}). start (); }}Das Ausführungsergebnis ist wie folgt: Thread 1 und Thread 2 Geben Sie gleichzeitig den Ausführungsstatus ein. Thread 2 führt schneller als Thread 1 aus, sodass Thread 2 zuerst ausgeführt wird. In diesem Prozess werden gleichzeitig Thread 1 und Thread 2 gleichzeitig ausgeführt.
Methode 1 Start
Methode 1 Ausführen
Methode 2 Start
Methode 2 Ausführen
Methode 2 Ende
Methode 1 Ende
2. Synchronisieren Sie gemeinsame Methoden:
Code -Snippet zwei:
Paket com.paddx.test.concurrent; öffentliche Klasse SynchronizedTest {public synchronisierte void methode1 () {System.out.println ("Methode 1 start"); try {System.out.println ("Methode 1 Execute"); Thread.sleep (3000); } catch (interruptedException e) {e.printstacktrace (); } System.out.println ("Methode 1 End"); } public synchronisierte void method2 () {System.out.println ("Methode 2 start"); try {System.out.println ("Methode 2 execute"); Thread.sleep (1000); } catch (interruptedException e) {e.printstacktrace (); } System.out.println ("Methode 2 End"); } public static void main (String [] args) {endgültig synchronisierte Test test = new SynchronizedTest (); neuer Thread (new Runnable () {@Override public void run () {Test.Method1 ();}}). start (); neuer Thread (new Runnable () {@Override public void run () {Test.Method2 ();}}). start (); }}Das Ausführungsergebnis ist wie folgt. Nach dem Vergleich mit dem Codesegment ist deutlich zu sehen, dass Thread 2 auf die Ausführung von Methode1 von Thread 1 warten muss, um die Methode für die Methode2 auszuführen.
Methode 1 Start
Methode 1 Ausführen
Methode 1 Ende
Methode 2 Start
Methode 2 Ausführen
Methode 2 Ende
3. Synchronisation der Statischen Methode (Klasse)
Code -Snippet Drei:
Paket com.paddx.test.concurrent; öffentliche Klasse SynchronizedTest {public static synchronisierte void methode1 () {System.out.println ("Methode 1 start"); try {System.out.println ("Methode 1 Execute"); Thread.sleep (3000); } catch (interruptedException e) {e.printstacktrace (); } System.out.println ("Methode 1 End"); } public static synchronisierte void method2 () {System.out.println ("Methode 2 start"); try {System.out.println ("Methode 2 execute"); Thread.sleep (1000); } catch (interruptedException e) {e.printstacktrace (); } System.out.println ("Methode 2 End"); } public static void main (String [] args) {endgültig synchronisierte Test test = new SynchronizedTest (); endgültig synchronisierte Test Test2 = neuer synchronisierter Test (); neuer Thread (new Runnable () {@Override public void run () {Test.Method1 ();}}). start (); neuer Thread (new Runnable () {@Override public void run () {Test2.Method2 ();}}). start (); }}Das Ausführungsergebnis ist wie folgt. Die Synchronisation statischer Methoden ist im Wesentlichen eine Synchronisation von Klassen (statische Methoden sind im Wesentlichen Klassenmethoden, nicht Methoden für Objekte). Selbst wenn Test und Test2 zu verschiedenen Objekten gehören, gehören daher beide zu Instanzen der synchronisierten Testklasse, sodass Methode1 und Method2 nur nacheinander ausgeführt werden können und nicht gleichzeitig ausgeführt werden können.
Methode 1 Start
Methode 1 Ausführen
Methode 1 Ende
Methode 2 Start
Methode 2 Ausführen
Methode 2 Ende
4. Codeblocksynchronisation
Code -Snippet vier:
Paket com.paddx.test.concurrent; öffentliche Klasse SynchronizedTest {public void method1 () {System.out.println ("Methode 1 start"); try {synchronized (this) {System.out.println ("Methode 1 execute"); Thread.sleep (3000); }} catch (interruptedException e) {e.printstacktrace (); } System.out.println ("Methode 1 End"); } public void method2 () {System.out.println ("Methode 2 start"); try {synchronized (this) {System.out.println ("Methode 2 execute"); Thread.sleep (1000); }} catch (interruptedException e) {e.printstacktrace (); } System.out.println ("Methode 2 End"); } public static void main (String [] args) {endgültig synchronisierte Test test = new SynchronizedTest (); neuer Thread (new Runnable () {@Override public void run () {Test.Method1 ();}}). start (); neuer Thread (new Runnable () {@Override public void run () {Test.Method2 ();}}). start (); }}Das Ausführungsergebnis ist wie folgt. Obwohl sowohl Thread 1 als auch Thread 2 die entsprechende Methode und die Startausführung eingeben, muss Thread 2 warten, bis die Ausführung der Synchronisationsblock in Thread 1 abgeschlossen ist, bevor Sie den Synchronisationsblock eingeben.
Methode 1 Start
Methode 1 Ausführen
Methode 2 Start
Methode 1 Ende
Methode 2 Ausführen
Methode 2 Ende
2. Synchronisierter Prinzip
Wenn Sie noch Fragen zu den oben genannten Ausführungsergebnissen haben, machen Sie sich keine Sorgen. Lassen Sie uns zunächst das Prinzip der synchronisierten und dann auf die oben genannten Fragen zurückblicken, um sie auf einen Blick zu sehen. Schauen wir uns zunächst an, wie die synchronisierte Code die Codeblöcke synchronisiert, indem sie den folgenden Code dekompilieren:
Paket com.paddx.test.concurrent; öffentliche Klasse SynchronizedDemo {public void methode () {synchronisierte (this) {System.out.println ("Methode 1 start"); }}}Dekompilierungsergebnis:
In Bezug auf die Rolle dieser beiden Anweisungen beziehen wir uns direkt auf die Beschreibung in der JVM -Spezifikation:
Überwachung:
Jedes Objekt ist einem Monitor zugeordnet. Ein Monitor ist nur dann gesperrt, wenn er einen Eigentümer hat. Der Thread, der Monitorenter ausführt, versucht, wie folgt die Besitz von dem mit Objectef verbundenen Monitor zu erlangen: • Wenn die Eingangszahl des mit Objectef zugeordneten Monitors Null ist, tritt der Thread in den Monitor ein und legt seine Eingangszahl auf einen ein. Der Thread ist dann der Eigentümer des Monitors. • Wenn der Thread bereits den mit ObjecteF verbundenen Monitor besitzt, wird der Monitor wieder eingegeben und seine Eingangszahl erhöht. • Wenn ein anderer Thread den mit Objectef verbundenen Monitor bereits besitzt, blockiert der Thread, bis die Eintrittsanzahl des Monitors Null ist, und versucht erneut, sich Eigentümer zu gewinnen.
Die allgemeine Bedeutung dieser Passage lautet:
Jedes Objekt verfügt über eine Monitorsperre (Monitor). Wenn der Monitor besetzt ist, wird er verschlossen. Wenn der Thread den Monitorenter -Befehl ausführt, versucht er, den Besitz des Monitors zu erhalten. Der Prozess ist wie folgt:
1. Wenn die Anzahl der Eingabe des Monitors 0 beträgt, tritt der Thread in den Monitor ein und setzt dann die Eintragsnummer auf 1, der Thread ist der Eigentümer des Monitors.
2. Wenn der Thread den Monitor bereits besitzt und nur wieder eingegeben wird, wird die Anzahl der Eingaben in den Monitor zu 1 hinzugefügt.
3. Wenn andere Threads den Monitor besetzt haben, tritt der Thread in einen Blockierungszustand ein, bis die Anzahl der Eintrittseingabe 0 0 beträgt, und versuchen Sie dann, den Besitz des Monitors erneut zu erhalten.
Monitorexit:
Der Thread, der Monitorexit ausführt, muss der Eigentümer des Monitors sein, der mit der von ObjecteF verwiesenen Instanz verbunden ist. Der Thread verringert die Eingangszahl des mit Objectef verbundenen Monitors. Wenn der Wert der Eingangszahl Null ist, verlässt der Thread den Monitor und ist nicht mehr sein Eigentümer. Andere Themen, die für den Eintritt in den Monitor blockieren, dürfen versuchen, dies zu tun.
Die allgemeine Bedeutung dieser Passage lautet:
Der Thread, der Monitorexit ausführt, muss der Eigentümer des Monitors sein, der Objectref entspricht.
Wenn die Anweisung ausgeführt wird, wird die Anzahl der Eingänge des Monitors um 1 reduziert. Wenn die Anzahl der Eingänge des Monitors nach dem Abbau von 1 ist, verlässt der Thread den Monitor und ist nicht mehr der Eigentümer dieses Monitors. Andere von diesem Monitor blockierte Themen können versuchen, den Eigentum an diesem Monitor zu erhalten.
In diesen beiden Beschreibungsabsätzen sollten wir in der Lage sein, das Implementierungsprinzip der Synchronisierung klar zu sehen. Die semantische zugrunde liegende Schicht von Synchronisierten wird durch ein Monitorobjekt abgeschlossen. Tatsächlich stützen sich Warten/Benachrichtigungen und andere Methoden auch auf Monitorobjekte. Aus diesem Grund können nur Methoden wie Warten/Benachrichtigungen in synchronisierten Blöcken oder Methoden aufgerufen werden, ansonsten wird eine Ausnahme von Java.lang.ImlogalMonitorStateException ausgeworfen.
Schauen wir uns die Dekompilierungsergebnisse der Synchronisationsmethode an:
Quellcode:
Paket com.paddx.test.concurrent; öffentliche Klasse SynchronizedMethod {public synchronisierte void method () {System.out.println ("Hello World!"); }}Dekompilierungsergebnis:
Nach den Ergebnissen der Dekompilierung zu urteilen, wird die Synchronisation der Methode nicht über die Anweisungen Monitorexit abgeschlossen (theoretisch kann sie auch durch diese beiden Anweisungen implementiert werden). Im Vergleich zu gewöhnlichen Methoden wird jedoch der ACC_SYNCHRONISCHE KISSION in seinen konstanten Pool hinzugefügt. JVM implementiert die Synchronisation von Methoden basierend auf dieser Kennung: Wenn die Methode aufgerufen wird, wird überprüft, ob das Acc_Synchronisierte Zugriffsflag der Methode festgelegt ist. Wenn der Ausführungs -Thread festgelegt wird, wird zunächst den Monitor erhalten und dann die Methodekörper ausführen, nachdem die Methode erfolgreich ausgeführt wurde. Nachdem die Methode ausgeführt wurde, wird der Monitor freigegeben. Während der Methodenausführung kann kein anderer Thread das gleiche Monitor -Objekt mehr erhalten. Tatsächlich gibt es keinen Unterschied im Wesentlichen, aber die Synchronisation der Methode ist ein implizite Weg, um sie zu erreichen, ohne dass durch Bytecode erfolgen muss.
3.. Erläuterung der Betriebsergebnisse
Mit einem Verständnis des Synchronprinzips können Sie es leicht lösen, indem Sie sich das obige Programm ansehen.
1. Codesegment 2 Ergebnisse:
Obwohl Method1 und Method2 unterschiedliche Methoden sind, werden beide Methoden synchronisiert und über dasselbe Objekt aufgerufen. Vor dem Aufrufen müssen Sie daher um das gleiche Objekt um das Schloss (Monitor) konkurrieren, damit Sie die Schlösser nur ausschließlich gegenseitig erhalten können. Daher können Methode1 und Method2 nur nacheinander ausgeführt werden.
2. Code -Segment 3 Ergebnisse:
Obwohl Test und Test2 zu verschiedenen Objekten gehören, gehören Test und Test2 zu verschiedenen Fällen derselben Klasse. Da Method1 und Method2 beide zu statischen Synchronisationsmethoden gehören, müssen Sie den Monitor in derselben Klasse abrufen (jede Klasse entspricht nur einem Klassenobjekt), sodass Sie nur nacheinander ausführen können.
3.. Codesegment 4 Ergebnisse:
Für die Synchronisation von Codeblöcken ist es im Wesentlichen erforderlich, den Monitor des Objekts in den Klammern nach dem synchronisierten Schlüsselwort zu erhalten. Da der Inhalt der Klammern in diesem Code dies ist und Methode1 und Method2 über dasselbe Objekt aufgerufen werden. Vor dem Eingeben des Synchronisationsblocks müssen Sie um die Schlösser desselben Objekts konkurrieren, sodass der Synchronisationsblock nur nacheinander ausgeführt werden kann.
Vier Zusammenfassung
Synchronisiert ist die am häufigsten verwendete Methode zur Gewindesicherheit in der gleichzeitigen Programmierung von Java und ist relativ einfach zu verwenden. Wenn wir jedoch seine Prinzipien ausführlich verstehen und ein gewisses Verständnis für zugrunde liegende Kenntnisse wie Monitor -Sperrs haben, kann es uns helfen, die synchronisierten Schlüsselwörter korrekt zu verwenden. Andererseits kann es uns auch helfen, die Parallelitätsprogrammiermechanismus besser zu verstehen. Sie können sich auch ruhig mit verschiedenen gleichzeitigen Problemen befassen, denen Sie im täglichen Leben begegnen.