0. Pionierfragescode
Der folgende Code zeigt einen Zähler, in dem sich ich gleichzeitig zwei Threads ansammeln und jeweils 1000.000 Mal durchführen. Das Ergebnis, das wir erwarten, ist definitiv i = 2000000. Nachdem wir es jedoch oft ausgeführt haben, werden wir feststellen, dass der Wert von Ich immer weniger als 2000000 sein wird. Dies liegt daran, dass wenn zwei Threads gleichzeitig schreiben, das Ergebnis eines der Threads den anderen überschreibt.
öffentliche Klasse AccountingSync implementiert runnable {static int i = 0; public void erhöht () {i ++; } @Override public void run () {für (int j = 0; j <1000000; j ++) {erhöht (); }} public static void main (String [] args) löst unterbrochene Ausnahme {AccountingSync AccountingSync = new AccountingSync (); Thread T1 = neuer Thread (AccountingSync); Thread T2 = neuer Thread (AccountingSync); t1.start (); t2.Start (); t1.join (); t2.join (); System.out.println (i); }} Um dieses Problem grundlegend zu lösen, müssen wir sicherstellen, dass mehrere Threads beim Betrieb vollständig synchronisiert werden müssen. Das heißt, wenn Thread A schreibt i, Thread B kann nicht nur schreiben, sondern auch nicht lesen.
1. Die Rolle synchronisierter Schlüsselwörter
Die Funktion des synchronisierten Schlüsselworts besteht tatsächlich darin, die Synchronisation zwischen Threads zu realisieren. Seine Aufgabe ist es, den synchronisierten Code zu sperren, so dass jeweils nur ein Thread den Synchronisationsblock eingeben kann, wodurch die Sicherheit zwischen Threads gewährleistet wird. Genau wie im obigen Code kann die I ++ - Operation nur von einem anderen Thread gleichzeitig ausgeführt werden.
2. Ausübung synchronisierter Schlüsselwörter
Geben Sie die Objektsperrung an: Sperren Sie das angegebene Objekt und geben Sie den Synchronisationscode -Block ein, um die Sperre des angegebenen Objekts zu erhalten
Direkt auf die Instanzmethode einwirken: Es ist gleichbedeutend mit der Verriegelung der aktuellen Instanz. Wenn Sie den Synchron -Code -Block eingeben, müssen Sie die Sperre der aktuellen Instanz erhalten (dies erfordert, dass Sie beim Erstellen von Thread dieselbe Runnable -Instanz verwenden müssen).
Direkt auf statische Methoden einwirken: Es ist gleichbedeutend mit der Verriegelung der aktuellen Klasse. Bevor Sie in den Synchroncode -Block eingeben, müssen Sie die Sperre der aktuellen Klasse erhalten.
2.1 Geben Sie das zu sperren Objekt an
Der folgende Code gilt synchronisiert mit einem bestimmten Objekt. Es gibt hier eine Notiz, dass das angegebene Objekt statisch sein muss. Andernfalls werden wir das Objekt nicht jedes Mal miteinander teilen, wenn wir einen Thread neu sind, sodass die Bedeutung des Sperrens nicht mehr existiert.
public class AccountingSync implementiert runnable {endgültig statisches Objekt Object = new Object (); statische int i = 0; public void erhöht () {i ++; } @Override public void run () {für (int j = 0; j <1000000; j ++) {synchronized (Objekt) {erhöht (); }}} public static void main (String [] args) löst unterbrochene Ausnahme aus {Thread t1 = neuer Thread (neuer AccountingSync ()); Thread T2 = neuer Thread (neuer Buchhaltungssync ()); t1.start (); t2.Start (); t1.join (); t2.join (); System.out.println (i); }} 2.2 Wirken Sie direkt auf die Instanzmethode
Das synchronisierte Schlüsselwort wirkt auf die Instanzmethode, dh vor der Eingabe der erhöhten () -Methode muss der Thread die Sperre der aktuellen Instanz erhalten. Dies erfordert, dass wir beim Erstellen der Thread -Instanz dieselbe Runningable -Objektinstanz verwenden. Andernfalls sind die Schlösser des Threads nicht in derselben Instanz, daher gibt es keine Möglichkeit, über das Problem der Verriegelung/Synchronisierung zu sprechen.
öffentliche Klasse AccountingSync implementiert runnable {static int i = 0; public synchronisierte void erhöht () {i ++; } @Override public void run () {für (int j = 0; j <1000000; j ++) {erhöht (); }} public static void main (String [] args) löst unterbrochene Ausnahme {AccountingSync AccountingSync = new AccountingSync (); Thread T1 = neuer Thread (AccountingSync); Thread T2 = neuer Thread (AccountingSync); t1.start (); t2.Start (); t1.join (); t2.join (); System.out.println (i); }} Bitte achten Sie auf die ersten drei Zeilen der Hauptmethode, um die korrekte Verwendung der Schlüsselwörter auf der Instanzmethode zu veranschaulichen.
2.3 direkt auf statische Methoden wirken
Um das synchronisierte Schlüsselwort auf die statische Methode anzuwenden, müssen die beiden Threads nicht auf dieselbe runnable Methode verweisen wie im obigen Beispiel. Da der Methodenblock die Sperre der aktuellen Klasse anfordern muss und nicht die aktuelle Instanz, können die Threads weiterhin korrekt synchronisiert werden.
öffentliche Klasse AccountingSync implementiert runnable {static int i = 0; public statische synchronisierte Leere erhöht () {i ++; } @Override public void run () {für (int j = 0; j <1000000; j ++) {erhöht (); }} public static void main (String [] args) löst InterruptedException aus {Thread t1 = neuer Thread (neuer AccountingSync ()); Thread T2 = neuer Thread (neuer Buchhaltungssync ()); t1.start (); t2.Start (); t1.join (); t2.join (); System.out.println (i); }}3.. Falsche Verriegelung
Aus dem obigen Beispiel wissen wir, dass wir, wenn wir eine Zähleranwendung benötigen, um die Richtigkeit der Daten zu gewährleisten, natürlich den Zähler sperren müssen, damit wir den folgenden Code schreiben können:
öffentliche Klasse BadlockonInteger implementiert Runnable {statische Integer i = 0; @Override public void run () {für (int j = 0; j <1000000; j ++) {synchronized (i) {i ++; }}} public static void main (String [] args) löst unterbrochene Ausnahme {badlockoninteger badlockoninteger = new badlockoninteger (); Thread T1 = neuer Thread (BadlockonInteger); Thread T2 = neuer Thread (BadlockonInteger); t1.start (); t2.Start (); t1.join (); t2.join (); System.out.println (i); }}Wenn wir den obigen Code ausführen, werden wir feststellen, dass die Ausgabe, die ich sehr klein ist. Dies bedeutet, dass der Thread nicht sicher ist.
Um dieses Problem zu erklären, müssen wir mit Integer beginnen: In Java ist Integer ein invariantes Objekt. Wenn ein Objekt erstellt wurde, kann es nicht geändert werden. Wenn Sie eine Integer = 1 haben, dann wird es immer 1 sein. Was ist, wenn Sie dieses Objekt wollen = 2? Sie können nur eine Ganzzahl nachstellen. Nach jedem i ++ entspricht es dem Aufruf der Wertschöpfungsmethode der Ganzzahl. Schauen wir uns den Quellcode der ValueOf -Methode von Integer an:
public static Integer valueOf (int i) {if (i> = integercache.low && i <= integercache.high) return IntegerCache.cache [i + (-integercache.low)]; Neue Ganzzahl zurückgeben (i);} Integer.ValueOf () ist eigentlich eine Fabrikmethode, die dazu neigt, ein neues Integer -Objekt zurückzugeben und den Wert an i zu kopieren;
Daher kennen wir den Grund für das Problem. Da zwischen mehreren Threads, da I ++ nach mir kommt, verweist ich auf ein neues Objekt, kann der Thread jedes Mal verschiedene Objektinstanzen laden, wenn er gesperrt ist. Die Lösung ist sehr einfach. Sie können es lösen, indem Sie eine der drei oben genannten Synchron -Methoden verwenden.