In Java kann das Schlüsselwort Synchronized für die Steuerung der Threadsynchronisation verwendet werden, um den sequentiellen Zugriff auf Schlüsselressourcen zu erreichen und Datenkonsistenz zu vermeiden, die durch die gleichzeitige Ausführung von Multi-Threaded verursacht werden. Das Synchronprinzip ist ein Objektmonitor (Lock). Nur der Thread, der den Monitor erwirbt, kann weiter ausgeführt werden, da der Thread es wartet, den Monitor zu erwerben. Jedes Objekt oder jede Klasse in Java hat ein Schloss zugeordnet. Für ein Objekt überwacht es die Instanzvariable dieses Objekts. Für eine Klasse überwacht es die Klassenvariable (eine Klasse selbst ist ein Objekt der Klassenklasse, sodass die mit der Klasse zugeordnete Sperre auch eine Objektschloss ist). Es gibt zwei Möglichkeiten, synchronisierte Schlüsselwörter zu verwenden: synchronisierte Methode und synchronisiertes Block. Beide Überwachungsbereiche sind mit einem eingeführten Objekt verbunden. Wenn es diesen Überwachungsbereich erreicht, sperrt das JVM das Referenzobjekt, und wenn es verlässt, wird die Sperre am Referenzobjekt freigegeben (die JVM löst die Sperre frei, wenn ein Ausnahmeausgang stattfindet). Objektschlösser sind interne Mechanismen von JVM. Sie müssen nur Synchronisationsmethoden oder Synchronisationsblöcke schreiben. Beim Betrieb von Überwachungsbereichen wird das JVM automatisch das Schloss erfasst oder freigeben.
Beispiel 1
Schauen wir uns zunächst das erste Beispiel an. In Java gibt es nur einen kritischen Bereich desselben Objekts, auf das gleichzeitig zugegriffen werden darf (alle nicht statischen synchronisierten Methoden):
Paket Concurrency; public class main8 {public static void main (String [] args) {Account = new Account (); Account.SetBalance (1000); Firma Company = New Company (Konto); Thread CompanyThread = neuer Thread (Unternehmen); Bank Bank = New Bank (Konto); Thread BankThread = neuer Thread (Bank); System.out.printf ("Konto: Anfangsaldo: %f/n", Account.getBalance ()); CompanyThread.start (); bankthread.start (); Versuchen Sie {// join () -Methode, die auf diese beiden Threads warten, um CompanyThread.join () zu vervollständigen; bankthread.join (); System.out.printf ("Konto: Endguthaben: %f/n", Account.getBalance ()); } catch (interruptedException e) {e.printstacktrace (); }}} /*Konto*/Klassenkonto {privater Doppelbilanz; /*Hinzufügen eingehender Daten zum Gleichgewicht hinzufügen*/ public synchronisierte void addAmount (doppelte Menge) {double tmp = balance; try {thread.sleep (10); } catch (interruptedException e) {e.printstacktrace (); } tmp += Menge; Balance = TMP; } /*Eingehende Daten aus dem Gleichgewicht abziehen* / public synchronisierte void subtractAmount (doppelte Menge) {double tmp = balance; try {thread.sleep (10); } catch (interruptedException e) {e.printstacktrace (); } tmp -= Menge; Balance = TMP; } public double getBalance () {Return Balance; } public void setBalance (Doppelbilanz) {this.balance = balance; }} /*Bank*/Class Bank Implements Runnable {privates Konto; öffentliche Bank (Kontokonto) {this.account = Konto; } @Override public void run () {für (int i = 0; i <100; i ++) {account.subractAmount (1000); }}} /*Company*/Class Company Implements Runnable {privates Konto; Öffentliche Firma (Konto Konto) {this.account = Konto; } @Override public void run () {für (int i = 0; i <100; i ++) {account.AddAmount (1000); }}}Sie haben eine Simulationsanwendung für Bankkonten entwickelt, die Guthaben auffüllen und abziehen kann. Dieses Programm lädt das Konto auf, indem Sie die addAmount () -Methode 100 -mal aufrufen, wobei jedes Mal 1.000 einlegt. Abzieht dann den Kontosaldo, indem Sie die SubtracctAmount () -Methode 100 -mal aufrufen und jedes Mal 1.000 abziehen. Wir gehen davon aus, dass der endgültige Kontostand des Kontos dem anfänglichen Restbetrag entspricht, und wir implementieren ihn über das synchronisierte Schlüsselwort.
Wenn Sie das gleichzeitige Zugriffsproblem der gemeinsam genutzten Daten anzeigen möchten, müssen Sie die synchronisierten Schlüsselwörter nur in den Erklärungen von addAmount () und subtractAmount () löschen. Ohne das synchronisierte Schlüsselwort ist der gedruckte Gleichgewichtswert nicht konsistent. Wenn Sie dieses Programm mehrmals ausführen, erhalten Sie unterschiedliche Ergebnisse. Da die JVM die Ausführungsreihenfolge der Threads bei jedem Ausführen nicht garantiert, lesen und ändern die Threads den Kontostand in verschiedenen Bestellungen, was zu unterschiedlichen Endergebnissen führt.
Die Methode eines Objekts wird mit dem synchronisierten Schlüsselwort deklariert und kann nur von einem Thread zugegriffen werden. Wenn Thread A eine Synchronisationsmethode syncMethoda () ausführt, möchte Thread B andere Synchronisationsmethoden syncMethodb () dieses Objekts ausführen, wird Thread B blockiert, bis Thread A den Zugriff vervollständigt. Wenn Thread B jedoch auf verschiedene Objekte derselben Klasse zugreift, wird kein Thread blockiert.
Beispiel 2
Demonstrieren Sie das Problem, dass statische synchronisierte Methoden und nicht statische synchronisierte Methoden auf demselben Objekt über mehrere Threads gleichzeitig zugegriffen werden können. Überprüfen Sie es.
Paket Concurrency; public class main8 {public static void main (String [] args) {Account = new Account (); Account.SetBalance (1000); Firma Company = New Company (Konto); Thread CompanyThread = neuer Thread (Unternehmen); Bank Bank = New Bank (Konto); Thread BankThread = neuer Thread (Bank); System.out.printf ("Konto: Anfangsaldo: %f/n", Account.getBalance ()); CompanyThread.start (); bankthread.start (); Versuchen Sie {// join () -Methode, die auf diese beiden Threads warten, um CompanyThread.join () zu vervollständigen; bankthread.join (); System.out.printf ("Konto: Endguthaben: %f/n", Account.getBalance ()); } catch (interruptedException e) {e.printstacktrace (); }}} /*Konto*/Class -Konto {/*Ändern Sie es hier in eine statische Variable*/private statische Doppelbilanz = 0; /*Hinzufügen eingehender Daten zum Ausgleichsbilanz, beachten Sie, dass sie mit statischen*/ öffentlichen statischen synchronisierten Hohlraum -AddAmount (doppelte Menge) {Double TMP = Balance; try {thread.sleep (10); } catch (interruptedException e) {e.printstacktrace (); } tmp += Menge; Balance = TMP; } /*Die eingehenden Daten aus dem Gleichgewicht abziehen* / public synchronisierte void subtractAmount (doppelte Menge) {double tmp = balance; try {thread.sleep (10); } catch (interruptedException e) {e.printstacktrace (); } tmp -= Menge; Balance = TMP; } public double getBalance () {Return Balance; } public void setBalance (Doppelbilanz) {this.balance = balance; }} /*Bank*/Class Bank Implements Runnable {privates Konto; öffentliche Bank (Kontokonto) {this.account = Konto; } @Override public void run () {für (int i = 0; i <100; i ++) {account.subractAmount (1000); }}} /*Company*/Class Company Implements Runnable {privates Konto; Öffentliche Firma (Konto Konto) {this.account = Konto; } @Override public void run () {für (int i = 0; i <100; i ++) {account.AddAmount (1000); }}}Ich habe gerade das statische Schlüsselwort hinzugefügt, um das Gleichgewicht im vorherigen Beispiel zu ändern, und die Methode addAmount () kann auch das statische Schlüsselwort ändern. Sie können die Ausführungsergebnisse selbst testen, und jede Ausführung hat ein anderes Ergebnis!
Eine Zusammenfassung: