0. Leitfragecode
Der folgende Code zeigt einen Zähler und zwei Threads führen gleichzeitig eine Akkumulationsoperation für i aus, wobei jeder davon 1.000.000 Mal ausgeführt wird. Das Ergebnis, das wir erwarten, ist definitiv i=2000000. Aber nachdem wir es mehrmals ausgeführt haben, werden wir feststellen, dass der Wert von i immer kleiner als 2000000 ist. Dies liegt daran, dass das Ergebnis eines Threads das andere überschreibt, wenn zwei Threads gleichzeitig in i schreiben 🎜>
public class AccountingSync implements Runnable { static int i = 0; public void increase() { i++; } @Override public void run() { for (int j = 0; j < 1000000; j++) { increase(); } } public static void main(String[] args) throws InterruptedException { AccountingSync accountingSync = new AccountingSync(); Thread t1 = new Thread(accountingSync); Thread t2 = new Thread(accountingSync); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); } }
Um dieses Problem grundsätzlich zu lösen, müssen wir sicherstellen, dass mehrere Threads vollständig synchronisiert sind, wenn sie auf i ausgeführt werden. Mit anderen Worten, Thread A schreibt bei der Synchronisierung Die Aufgabe besteht darin, den synchronisierten Code zu sperren, sodass jeweils nur ein Thread den Synchronisationsblock betreten kann, wodurch die Sicherheit zwischen Threads gewährleistet wird. Genau wie im obigen Code kann die i++-Operation nur gleichzeitig ausgeführt werden Ein Thread wird ausgeführt.
2. Verwendung des synchronisierten Schlüsselworts
Objektsperre angeben: Sperren Sie das angegebene Objekt und geben Sie den synchronisierten Codeblock ein, um das angegebene Objekt zu erhalten. Die Sperre
wirkt direkt darauf Die Instanzmethode: Sie entspricht dem Sperren der aktuellen Instanz. Das Eingeben des Synchronisierungscodeblocks erfordert das Erhalten der Sperre der aktuellen Instanz (dies erfordert, dass beim Erstellen des Threads dieselbe ausführbare Instanz verwendet werden muss).
Es gilt der folgende Code Hier ist zu beachten, dass das angegebene Objekt statisch sein muss. Andernfalls wird das Objekt nicht jedes Mal miteinander geteilt, und die Bedeutung des Sperrens ist unterschiedlich wird nicht mehr existieren.
public class AccountingSync implements Runnable { final static Object OBJECT = new Object(); static int i = 0; public void increase() { i++; } @Override public void run() { for (int j = 0; j < 1000000; j++) { synchronized (OBJECT) { increase(); } } } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new AccountingSync()); Thread t2 = new Thread(new AccountingSync()); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); } }2.2 Direkt auf Instanzmethoden einwirken
Das synchronisierte Schlüsselwort wirkt sich auf Instanzmethoden aus, was bedeutet, dass der Thread vor dem Aufrufen der raise()-Methode die Sperre der aktuellen Instanz erhalten muss. Dies erfordert, dass wir beim Erstellen einer Thread-Instanz dieselbe ausführbare Objektinstanz verwenden , die Thread-Sperren befinden sich nicht auf derselben Instanz und es gibt keine Möglichkeit, über Sperr-/Synchronisierungsprobleme zu sprechen.
Bitte beachten Sie Präfix der Hauptmethode. Drei Zeilen, die die korrekte Verwendung von Schlüsselwörtern für Instanzmethoden erläutern.
public class AccountingSync implements Runnable { static int i = 0; public synchronized void increase() { i++; } @Override public void run() { for (int j = 0; j < 1000000; j++) { increase(); } } public static void main(String[] args) throws InterruptedException { AccountingSync accountingSync = new AccountingSync(); Thread t1 = new Thread(accountingSync); Thread t2 = new Thread(accountingSync); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); } }
2.3 Direktes Einwirken auf statische Methoden
Verwenden Sie das synchronisierte Schlüsselwort für statische Methoden Dies ist nicht erforderlich. Im Beispiel müssen zwei Threads auf dieselbe Runnable-Methode verweisen. Da der Methodenblock die Sperre der aktuellen Klasse und nicht der aktuellen Instanz anfordern muss, können die Threads weiterhin korrekt synchronisiert werden 🎜>
public class AccountingSync implements Runnable { static int i = 0; public static synchronized void increase() { i++; } @Override public void run() { for (int j = 0; j < 1000000; j++) { increase(); } } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new AccountingSync()); Thread t2 = new Thread(new AccountingSync()); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); } }Wenn wir den obigen Code ausführen , wir werden feststellen, dass die i-Ausgabe sehr klein ist. Dies zeigt, dass Threads nicht sicher sind. Um dieses Problem zu erklären, müssen wir mit Integer beginnen: In Java ist Integer ein unveränderliches Objekt. Sobald das Objekt erstellt wurde, kann es nicht mehr geändert werden. Wenn Sie eine Ganzzahl = 1 haben, ist es immer 1. Was ist, wenn Sie dieses Objekt = 2 möchten? Nach jedem i++ ist es äquivalent zum Aufrufen der valueOf-Methode von Integer. Schauen wir uns den Quellcode der valueOf-Methode von Integer an:
public class BadLockOnInteger implements Runnable { static Integer i = 0; @Override public void run() { for (int j = 0; j < 1000000; j++) { synchronized (i) { i++; } } } public static void main(String[] args) throws InterruptedException { BadLockOnInteger badLockOnInteger = new BadLockOnInteger(); Thread t1 = new Thread(badLockOnInteger); Thread t2 = new Thread(badLockOnInteger); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(i); } }Integer.valueOf() ist Eigentlich eine Factory-Methode, die tendenziell ein neues Integer-Objekt zurückgibt und den Wert erneut nach i kopiert.
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }Wir kennen also die Ursache des Problems, da zwischen mehreren Threads nach i++ gilt. i zeigt auf ein neues Objekt. Daher kann es sein, dass der Thread bei jeder Sperre eine andere Objektinstanz lädt. Die Lösung ist sehr einfach: Verwenden Sie einfach eine der drei oben genannten Synchronisierungsmethoden
Weitere Artikel zur Verwendung von Java-synchronisierten Schlüsselwörtern finden Sie auf der chinesischen PHP-Website!