Das synchronisierte Schlüsselwort ist eine Synchronisationssperre, die häufig in der gleichzeitigen Java-Programmierung verwendet wird. Es wird zum Sperren von Methoden oder Codeblöcken verwendet und kann synchronisiert (dieses) {}, synchronisiert (Objekt) {} sein (Klasse) {}.
Die Sperre wird automatisch aufgehoben, wenn der gesperrte Inhalt ausgeführt wird oder während der Ausführung eine Ausnahme ausgelöst wird.
Wenn Sie die Sperre manuell aufheben möchten, müssen Sie die Methode wait() des gesperrten Objekts aufrufen, um die Sperre aufzuheben und in einen Wartezustand zu versetzen, zu anderen Threads zur Ausführung wechseln und benachrichtigen Die ()-Methode weckt nur das aufgerufene Objekt der wait()-Methode anderer Threads, die Sperre wird jedoch nicht aufgehoben, und die Auswahlreihenfolge wird nicht durch den Code gesteuert, sondern von der virtuellen Maschine implementiert.
Daher können die Methoden wait(), notify() und notifyAll() des Objekts nur mit dem synchronisierten Schlüsselwort verwendet werden, um die Planung zwischen Threads abzuschließen.
wobei die gesperrte Methode äquivalent zu synchronisiert(this){all code of the method as a code block} ist, wie folgt:
public synchronized void test() { ... }
ist äquivalent zu
public void test() { synchronized (this) { ... } }
gesperrt Im obigen Beispiel handelt es sich um ein Objekt dieser Klasse. Wir wissen, dass die statische Methode zur Klasse und nicht zum Objekt gehört. Daher sperrt die durch die Synchronisierung geänderte statische Methode alle Objekte dieser Klasse Das heißt, auch zwei Instanzobjekte werden gesperrt, solange sie dieser Klasse angehören.
public synchronized static void test() { ... }
entspricht
public static void test() { synchronized (所在类.class) { ... } }
Ob es sich um eine Sperrmethode oder einen Codeblock handelt, unabhängig vom Referenzobjekt beim Sperren des Codeblocks ist es klar, solange Sie sich an eines erinnern Prinzip, das heißt, wenn das Referenzobjekt synchronisiert ist, funktionieren Sperren nur, wenn sie gleich sind. Andernfalls schließen sich die Sperren nicht gegenseitig aus und können gleichzeitig ausgeführt werden.
synchronized(this) zeigt an, dass die Sperre wirksam wird, wenn die Objektinstanzen der aktuellen Klasse gleich sind, synchronisiert(Object) zeigt an, dass die Sperre wirksam wird, wenn die Objektobjekte gleich sind, und synchronisiert(class ) zeigt an, dass die Sperre wirksam wird, wenn alle derselben Klasse angehören.
Geben Sie ein einfaches Beispiel:
public class TestController { public class Task implements Runnable{ private String str; Task(String str){ this.str=str; } @Override public void run() { synchronized (str) { try { Thread.sleep(3000l); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(str); } } } public static void main(String[] args) throws InterruptedException { TestController testController = new TestController(); Thread thread1 = new Thread(testController.new Task("1")); Thread thread2 = new Thread(testController.new Task("1")); thread1.start(); thread2.start(); } }
Im obigen Code ist das Referenzobjekt str alles „1“. In Java, wenn die Zeichenfolge this.str="1" übergibt, Zuweisung entspricht str=String.valueOf("1"). Wenn die Zeichenfolge "1" zuvor initialisiert wurde, wird die vorherige direkt übernommen, es handelt sich also um dasselbe Objekt. Gemäß dem oben eingeführten Prinzip wird die Sperre wirksam, sodass nach 3 Sekunden 1 und nach weiteren 3 Sekunden 1 ausgegeben wird.
Wenn Thread2 in
Thread thread2 = new Thread(testController.new Task("2"));
geändert wird, ist eines der Referenzobjekte „1“ und das andere „2“, die nicht dasselbe Objekt sind, sodass die Sperre nicht gegenseitig erfolgt exklusiv funktioniert, das Ergebnis ist, dass 1 und 2 nach 3 Sekunden fast gleichzeitig ausgegeben werden.
Bei all dem handelt es sich um mehrere Threads, die gleichzeitig dieselbe Methode aufrufen. Was ist, wenn verschiedene Methoden aufgerufen werden?
public class Test{ public synchronized void m1(){ System.out.println("m1 running..."); try { Thread.sleep(3000l); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("m1 end"); } public synchronized void m2(){ System.out.println("m2 running..."); System.out.println("m2 end"); } public static void main(String[] args) { Test test = new Test(); new Thread(new Runnable() { @Override public void run() { test.m1(); } }).start(); new Thread(new Runnable() { @Override public void run() { test.m2(); } }).start(); } }
Das Ausgabeergebnis des obigen Codes ist:
m1 running... //过3秒 m1 end m2 running... m2 end
Wie oben erwähnt, entspricht die synchronisierte Änderung synchronisiert(this) {dem gesamten Code der Methode als Codeblock} und Dies stellt ein Objekt dar, das heißt, der erste Thread erhält die Sperre des Testobjekts. Da alle Objekte gleich sind, kann der zweite Thread die Sperre nicht erhalten und ist blockiert.
Ändern Sie das obige Beispiel wie folgt:
private String str = "1"; public void m1(){ synchronized(str){ System.out.println("m1 running..."); try { Thread.sleep(3000l); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("m1 end"); } } public void m2(){ synchronized(str){ System.out.println("m2 running..."); System.out.println("m2 end"); } }
Wenn der erste Thread m1() aufruft, erhält er die Sperre des Objekts str. Der zweite Thread benötigt sie auch, wenn er m2( ). Erhalten Sie die Sperre des Objekts str. Da es sich um dasselbe Testobjekt handelt, sind die beiden Stränge auch dasselbe Objekt, sodass der zweite Thread blockiert wird, da er die Sperre nicht erhalten kann und das Ausgabeergebnis dasselbe ist wie das vorherige Beispiel.
Wenn Sie das obige Beispiel wie folgt ändern:
public class M1 { public void m(String str){ synchronized (str) { System.out.println("m1 runing"); try { Thread.sleep(3000l); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("m1 end"); } } } public class M2 { public void m(String str){ synchronized (str) { System.out.println("m2 runing"); System.out.println("m2 end"); } } } public class Test { public static void main(String[] args) { String str = "1"; new Thread(new Runnable() { @Override public void run() { new M1().m(str); } }).start(); new Thread(new Runnable() { @Override public void run() { new M2().m(str); } }).start(); } }
Die dieses Mal aufgerufene Methode ist in zwei Klassen, aber das Ergebnis ist das gleiche wie in den beiden vorherigen Beispielen, da sie gesperrt sind Alle übergebenen str-Objekte. Es gibt nur eine Sperre für dasselbe Objekt. Wenn der erste Thread sie übernimmt, kann der zweite Thread nur warten.
Zusammenfassung:
A. Unabhängig davon, ob das synchronisierte Schlüsselwort einer Methode oder einem Objekt hinzugefügt wird, gilt die Sperre, wenn das Objekt, auf das es wirkt, nicht statisch ist Es erhält das Objekt. Wenn das Objekt, auf das synchronisiert wird, eine statische Methode oder eine Klasse ist, gilt die Sperre, die es erhält, für die Klasse, und alle Objekte der Klasse haben dieselbe Sperre.
B. Jedem Objekt ist nur eine Sperre zugeordnet. Wer diese Sperre erhält, kann den von ihm gesteuerten Code ausführen.
C. Die Implementierung der Synchronisierung erfordert einen hohen Systemaufwand und kann sogar zu einem Deadlock führen. Vermeiden Sie daher unnötige Synchronisierungskontrollen
Das obige ist der detaillierte Inhalt vonVerwendung des synchronisierten Schlüsselworts. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!