Schlüsselwort synchronisiert
Der synchronisierte Schlüssel kann Funktionen und Aussagen innerhalb von Funktionen ändern. Unabhängig davon, ob es zu Methoden oder Objekten hinzugefügt wird, ist das Sperren ein Objekt, anstatt ein Stück Code oder eine Funktion als Sperre zu behandeln.
1. Wenn zwei gleichzeitige Threads auf den synchronisierten (diesen) synchronisierten Codeblock in demselben Objekt zugreifen, kann nur ein Thread für einen bestimmten Zeitraum ausgeführt werden, und der andere Thread kann diesen Code nur ausführen, nachdem der aktuelle Thread die Ausführung abgeschlossen hat.
2. Wenn ein Thread in einem Objekt auf einen synchronisierten (dieses) synchronisierten Codeblocken zugreift, können andere Threads in diesem Objekt weiterhin auf andere nicht synchronisierte (dieser) Codeblöcke zugreifen.
3.. Es ist hier zu beachten, dass wenn ein Thread auf einen synchronisierten (diesen) Codeblock eines Objekts zugreift, andere Threads aus dem Zugriff auf andere synchronisierte (dieses) synchronisierte Codeblöcke in diesem Objekt blockiert werden.
4. Die oben genannten gilt auch für andere Synchronisationscodeblöcke, dh wenn ein Thread auf einen synchronisierten (this) -Synchronisationscodeblock eines Objekts zugreift, erhält der Thread die Objektsperrung des Objekts. Darüber hinaus entspricht jedes Objekt (d. H. Klasseninstanz) einer Sperre. Es ist blockiert. Dieser Mechanismus sorgt dafür .
Nachteile der synchronisierten Methode:
Da synchronisiert das Objekt sperrt, das diese Synchronisationsmethode aufruft, dh wenn ein Thread P1 diese Methode in verschiedenen Threads ausführt, bilden sie gegenseitige Ausschlüsse, wodurch der Effekt der Synchronisation erreicht wird. Es ist jedoch hier zu beachten, dass ein weiteres Objekt der Klasse, das dieses Objekts ist, diese Methode willkürlich mit dem hinzugefügten synchronisierten Schlüsselwort aufrufen kann. Das Wesen der Synchronisationsmethode besteht darin, die Synchroneinheit auf die Objektreferenz anzuwenden. Dieser Fall. Wir werden diese Situation im Folgenden im Detail erklären:
Führen Sie zunächst zwei gesperrte Objekte mit dem synchronisierten Schlüsselwort und der Klasse ein - synchronisierte Synchronen können zu Ressourcen hinzufügen. Von dieser Klasse können andere Objekte dieser Klasse weiterhin die synchronisierte Methode verwenden, die das vorherige Objekt gesperrt hat.
Eines der Hauptprobleme, die wir hier diskutieren, lautet: "Wird dieselbe Klasse und verschiedene Instanzen dieselbe Methode nennen, wird es ein Synchronisierungsproblem geben?"
Das Synchronisationsproblem hängt nur mit der Ressource zusammen und hängt davon ab, ob die Ressource statisch ist. Für die gleichen statischen Daten gehört die gleiche Funktion zu verschiedenen Threads, um sie gleichzeitig zu lesen und zu schreiben, und die CPU wird keine Fehler erzeugen. Sie. Selbst wenn Sie zwei verschiedene Codes haben, die in zwei verschiedenen Kernen der CPU ausgeführt werden und gleichzeitig eine Speicheradresse schreiben, sperrt der Cache -Mechanismus zuerst einen in L2. Dann aktualisieren und teilen Sie es mit einem anderen Kern, und es wird keine Fehler geben, sonst sind Intel oder AMD umsonst.
Solange Sie nicht die gleiche Ressource oder Variable haben wie zwei Code -Freigaben, gibt es keine Datenkonsistenz. Darüber hinaus haben Aufrufe an verschiedene Objekte derselben Klasse völlig unterschiedliche Stapel und sind völlig irrelevant.
Hier verwenden wir ein Beispiel, um den Ticketverkaufsprozess zu veranschaulichen, bei dem unsere gemeinsam genutzten Ressourcen die verbleibende Anzahl von Tickets sind.
Paket Com.Test; öffentlicher Klasse Threadsafetest erweitert Thread implementiert Runnable {private statische int num = 1; void Sell (String Name) {if (num> 0) {System. (Abgeschlossen in 5 Sekunden). ; ("System: Aktuelle Anzahl der Stimmen:" + num); args []) {try {neuer Threadsafetest ("Ticketverkäufer li xx") .Start (); {e.printstacktrace (); Führen Sie den obigen Code aus und die Ausgabe, die wir erhalten, ist:
Ticketleiter Li XX: Die Anzahl der Testkarten ist größer als 0 Ticketleiter Li XX: Die Zahlung wird gesammelt (abgeschlossen in etwa 5 Sekunden). . . Ticket -Dirigent King X: Die Anzahl der Testkarten ist größer als 0 Ticket Dirigent King X: Die Zahlung wird gesammelt (abgeschlossen in etwa 5 Sekunden). . . Ticketverkäufer Li XX: Drucken Sie die Rechnung, Abschlusssystem für Ticketverkäufe: Aktuelle Anzahl der Stimmen: 0 Ticketverkäufer Wang X: Drucken Sie die Rechnung, Ticketverkaufssystem: Aktuelle Anzahl der Stimmen: -1 Warnung: Die Anzahl der Stimmen ist niedriger als 0, negative Zahlen erscheinen
Basierend auf den Ausgabeergebnissen können wir feststellen, dass die verbleibenden Stimmen -1 und ein Synchronisationsfehlerproblem vorliegen. Der Grund dafür ist, dass die beiden von uns erstellten Instanzobjekte die gemeinsam genutzte statische Ressource statische Int num = 1 gleichzeitig geändert haben. Anschließend entfernen wir den Modifikator statisch in der Box im obigen Code und führen dann das Programm aus, um zu erhalten:
Ticketleiter Li XX: Die Anzahl der Testkarten ist größer als 0 Ticketleiter Li XX: Die Zahlung wird gesammelt (abgeschlossen in etwa 5 Sekunden). . . Ticket -Dirigent King X: Die Anzahl der Testkarten ist größer als 0 Ticket Dirigent King X: Die Zahlung wird gesammelt (abgeschlossen in etwa 5 Sekunden). . . Ticketverkäufer Li XX: Drucken Sie die Rechnung, Ticketverkaufssystem: Aktuelle Anzahl der Tickets: 0 Ticketverkäufer Wang X: Drucken Sie die Rechnung, Ticketverkaufssystem: Aktuelle Anzahl der Tickets: 0
Nach dem Ändern des Abschlusses läuft das Programm ohne Probleme. Dies widerspricht jedoch unserer Erwartung, dass mehrere Threads gleichzeitig gemeinsam genutzte Ressourcen verarbeiten können (nach statischer NUM ändert sich Num von gemeinsamen Ressourcen zu Mitgliedern Variablen, die jeder Instanz gehören), was offensichtlich nicht das ist, was wir wollen.
In den beiden oben genannten Codes ist die Hauptsache, das Objekt zu sperren. Aus dem Grund, warum ich bereits erwähnt habe, wird die CPU die Logik des Programms standardmäßig für die gleiche gemeinsame Ressource verändert. Daher müssen wir den Umfang des Schlosses ändern. Klasse und die gemeinsamen Ressourcen gleichzeitig.
Paket Com.Test; öffentlicher Klasse Threadsafetest erweitert Thread implementiert Runnable {private statische int num = 1; statische void Sell (String -Name) {if (num> 0) {System. Sammeln Sie Zahlung (ca. 5 Sekunden). (); .println ("System: aktuelle Stimmenzahl:" + num); String args []) {try {neuer Threadsafetest ("Ticketverkäufer li xx") .Start (); ) {e.printstacktrace (); Machen Sie das Programm wie oben, um das Run -Ergebnis zu erhalten:
Ticketleiter Li XX: Die Anzahl der Testkarten ist größer als 0 Ticketleiter Li XX: Die Zahlung wird gesammelt (abgeschlossen in etwa 5 Sekunden). . . Ticketverkäufer Li XX: Drucken Sie das Ticket, Ticketverkaufssystem: Aktuelle Anzahl der Tickets: 0 Ticketverkäufer Wang X: Keine Tickets, Stopp -Ticketverkauf
Ein statischer Modifikator wird der Sell () -Methode hinzugefügt, sodass das Objekt des Schlosses zu einer Klasse wird. Dies wird die gewünschten Ergebnisse wie erwartet erhalten.
Zusammenfassen:
1. Es gibt zwei Verwendungen synchronisierter Schlüsselwörter: synchronisierte Methode und synchronisiertes Block.
2. In Java ist es nicht nur eine Klasseninstanz, sondern jede Klasse kann auch einem Schloss entsprechen.
1. Das synchronisierte Schlüsselwort kann nicht vererbt werden. Obwohl synchronisiert werden kann, um Methoden zu definieren, gehört synchronisiert nicht zu einem Teil der Methodendefinition, sodass das synchronisierte Schlüsselwort nicht vererbt werden kann. Wenn eine Methode in der übergeordneten Klasse das synchronisierte Schlüsselwort verwendet und die Unterklasse diese Methode ebenfalls überschreibt, wird die Methode in der Unterklasse nicht synchronisiert und muss die Methode in der Unterklasse hinzufügen. . Natürlich können Sie auch die entsprechenden Methoden in der übergeordneten Klasse in der Unterklasse aufrufen. synchronisiert werden. wie,
Fügen Sie der Unterklasse synchronisiertes Schlüsselwort hinzu:
Klasse Eltern {public synchronisierte void method () {}} Klasse Child erweitert übergeordnete {public Synchronisierte void -Methode () {}} Rufen Sie die übergeordnete Klassenmethode an:
Klasse Eltern {public synchronisierte void method () {}} Klasse Child erweitert übergeordnete {public void method () {Super.Method (); 2. Das synchronisierte Schlüsselwort kann bei der Definition der Schnittstellenmethode nicht verwendet werden.
3. Der Konstruktor kann das synchronisierte Schlüsselwort nicht verwenden, aber der synchronisierte Block kann zur Synchronisation verwendet werden.
V.
5. Das synchronisierte Schlüsselwort kann nicht zum Synchronisieren von Variablen verwendet werden, z. B. der folgende Code ist falsch:
public synchronisierte int n = 0;
6. Obwohl die Verwendung von synchronisiertem Schlüsselwort die sicherste Synchronisationsmethode ist, wird es auch zu unnötigen Ressourcenverbrauch und Leistungsverlusten führen. Auf der Oberfläche sperrt synchronisiert eine Methode, aber tatsächlich wird die synchronisierte Schlüsselwort für nicht statische Methoden verwendet, und die methodische Methode kann bei der Ausführung einer der Methoden nicht sein. ausgeführt. Statische Methoden ähneln nicht statische Methoden. Statische Methoden und nicht-statische Methoden beeinflussen sich jedoch nicht gegenseitig. Siehe der folgende Code:
öffentliche Klasse MyThead1 erweitert Thread {public String methodName; ");} public synchronisierte void methode2 () {methode (" Nichtstatische Methode2-Methode ");} öffentliche statische synchronisierte Void-Methode3 () {Methode (" statische Methode3-Methode ");} öffentliche statische statische synchronisierte Void-Methode4 () {Methode ("statische Methode 4"); ) Ausnahme {MyThread1 MyThread1 = New MyThead1 (); ) Schlaf (100); Das Betriebsergebnis ist:
Nicht statische Methode1-Methode statische Methode3 Methode
Aus den oben genannten Run -Ergebnissen können wir feststellen, dass Methode2 und Method4 erst ausgeführt werden, wenn Method1 und Method3 abgeschlossen sind. Daher können wir die Schlussfolgerung ziehen, dass wenn wir synchronisiert sind, um nicht statische Methoden in der Klasse zu definieren, alle synchronisierten definierten nicht-statischen Methoden in dieser Klasse beeinflusst. in dieser Klasse statische Methode definiert durch synchronisiert. Dies ist ein bisschen wie eine Tabellensperrung in einer Datentabelle. Daher wird die starke Verwendung dieser Synchronisationsmethode die Leistung des Programms erheblich verringern.
Tipps für den sichereren Zugang zu gemeinsamen Ressourcen:
1. Definieren Sie die Instanzvariable von privat + seine GET -Methode, anstatt die Instanzvariable von öffentlich/geschützt zu definieren. Wenn eine Variable als öffentlich definiert wird, kann das Objekt sie direkt durchführen, um die Kontrolle der Synchronisationsmethode in der Außenwelt zu umgehen und sie zu ändern. Dies ist auch eine der Standardimplementierungen von Javabäen.
2. Wenn die Instanzvariable ein Objekt ist, wie z. Die private Variable ist auch geändert, wäre sie nicht sehr gefährlich? Zu diesem Zeitpunkt müssen Sie synchronisiert hinzugefügt, um Methode zu erhalten und nur Clone () dieses privaten Objekts zurückzugeben. Auf diese Weise erhält der Anrufer nur ein Verweis auf die Objektkopie.
Drei Möglichkeiten, um Objektmonitor (Sperre) zu erhalten und zu benachrichtigen ()
In einer Thread -Methode müssen Aufrufe zum Warten () und benachrichtigen () ein Objekt angeben, und der Thread muss den Monitor des Objektobjekts haben. Der einfachste Weg, um den Objektmonitor zu erhalten, besteht darin, das synchronisierte Schlüsselwort für das Objekt zu verwenden. Nach dem Aufrufen der Wait () -Methode löst der Thread das Objektschloss frei und tritt in den Schlafzustand ein. Wenn andere Threads die mellify () -Methode aufrufen, muss dasselbe Objektobjekt verwendet werden.
Bei mehreren von einem Objekt gesperrten Methoden wird eine davon ausgewählt, um beim Aufrufen der Notify () -Methode aufzuwachen, und notifyAll () wird alle seine wartenden Threads aufwachen.
paket net.mindview.util; import Javax.swing.jframe; public class waitandnotify {public static void main (String [] args) {System out.println ("Hallo Welt!"); Frame.SetDefaultCloseOperation (JFrame. 300, 100) SetLocation (250, 250); {t = neuer WaitNotifyThead (WaitNotifyJframe) .Add (Start); (Pause); .Add (Ende); = f; if (iswait) wait (); Wie im Code im obigen Beispielfeld wird die Ausführung eine Ausnahme von java.lang.ImlogalMonMonitoritateException ausgelöst, wenn der synchrone Codeblock entfernt wird.
Wenn wir uns die JDK ansehen, können wir sehen, dass der Grund für diese Ausnahme darin besteht, dass der aktuelle Thread nicht der Eigentümer dieses Objektmonitors ist.
Diese Methode sollte nur von einem Thread aufgerufen werden, der der Eigentümer dieses Objektmonitors ist.
1. Durch Ausführen der synchronen Instanzmethode dieses Objekts, wie z. B.:
public synchronisierte void n () {benachrichtigen (); 2. Durch Ausführung des Körpers der synchronisierten Anweisung, die in diesem Objekt synchronisiert ist, wie z. B.:
public void n () {synchronized (this) {benachrichtigen ();}}; 3. Für Objekte vom Klassentyp können Sie synchrone statische Methoden dieser Klasse ausführen.
Wenn wir eine statische Methode aufrufen, erstellen wir nicht unbedingt ein Instanzobjekt. Daher kann dies nicht verwendet werden, um statische Methoden zu synchronisieren, daher muss das Klassenobjekt zur Synchronisierung statischer Methoden verwendet werden. Ein weiteres Beispiel, um zu veranschaulichen:
öffentliche Klasse SynchronizedStatic Implements Runnable {private static boolean Flag = true; // Klassenobjekt -Synchronisierungsmethode eins: // Achten Sie auf die Synchronisationsmethode der statischen Modifikation, Monitor: i = 0; i <100; // Klassenobjekt -Synchronisierungsmethode 2: private void TestsyncBlock () {// Anzeige verwendet die GET -Klasse als Monitor. Es ist dasselbe, wie die statische synchronisierte Methode implizit den Klassenmonitor erhält. synchronisierte (synchronisierte statische Klasse) {für (int i = 0; i <100; i ++) {try {thread ("TestsyncBlock:" + i); Daher führen verschiedene Threads unterschiedliche Methoden aus, und nur auf diese Weise können Sie unterschiedliche Verriegelungseffekte sehen. if (Flag) {Flag = False; = SynchronizedStatic (); Der obige Code führt das Ergebnis des Ausführens von zwei Synchronisationsmethoden aus, um 100 bis 99 gleichzeitig zu drucken. Block ist eine Klasse. Die beiden Methoden sind ähnlich. Da der Umfang der Methode eins und die Methode beide Klassen sind, werden sie sich gegenseitig ausschließen. Daher wird das laufende Ergebnis des Programms:
TestsyncMethod: 0TestSyncMethod: 1 ... ... TestsyncMethod: 99TestSyncBlock: 0 ... ... TestsyncBlock: 99
Wenn wir jedoch synchronisierte Statisch ersetzen.
TestsyncBlock: 0TestSyncMethod: 0TestSyncBlock: 1TestSyncMethod: 1 ... ... TestsyncMethod: 99TestSyncBlock: 99
Es gibt zwei Sperrbereiche, eines ist das Objekt der Klasse und die andere ist die Klasse selbst. Im obigen Code werden zwei Methoden angegeben, um den Umfang der Sperre eine Klasse zu ermöglichen, damit die Synchronisation zwischen verschiedenen Objekten derselben Klasse abgeschlossen werden kann.
Um die oben genannten zusammenzufassen, müssen die folgenden Punkte beachtet werden:
1.. Wait (), benachrichtigen () und notifyAll () müssen alle unter der Prämisse der Objektmonitor ausgeführt werden, sonst wird ein Java.lang.ImleGalMonitorStateException geworfen.
2. Mehrere Threads können gleichzeitig auf einem Objekt warten.
3. Benachrichtigung () soll zufällig einen Thread auf das Objekt warten.
4. Der von Notify () geweckte Thread wacht nicht sofort nach der Ausführung von Benachrichtigung () auf, sondern erst nach der Veröffentlichung des Objektmonitors von Benachrichtigung ().
5. Diese Objektmethoden sind immer noch weit vom Schlaf- und Interrupt -Methoden von Thread entfernt. Verwechseln Sie sie also nicht.