Plötzlich habe ich festgestellt, dass es noch viele Dinge gibt, die ich verstehen muss, wie zum Beispiel die Verwendung des Schlüsselworts „synced“. Denn als ich gestern über die Erstellung von Verbindungspool-Sockets nachgeforscht habe, habe ich herausgefunden, dass ich das nicht tun kann. Da ich dieses Konzept nicht verstand, war es unmöglich, überhaupt fortzufahren, also beschloss ich, meine Liebe zu Socket abzukühlen und auf synchronisierte zurückzublicken.
Nachdem ich den ganzen Morgen Think in Java gelesen habe, denke ich, dass es immer noch sehr effektiv ist und sollte sofort geschrieben werden, um meinen Eindruck zu vertiefen. Ich habe das Gefühl, dass die Wiederverwendbarkeit meines Gehirns extrem gering ist und ich immer neue Speicherobjekte generieren muss, was eine Menge sich wiederholender Aufgaben erfordert Es sollten Aufzeichnung, Analyse und Zusammenfassung durchgeführt werden.
Um die Verwendung von „Synchronisiert“ zu verstehen, müssen Sie zunächst wissen, welches Problem damit gelöst wird. Da „Synchronisiert“ natürlich Synchronisierung bedeutet, wird es zur Lösung des Problems verwendet Hier ist ein Beispiel für eine möglicherweise auftretende Synchronisation.
In diesem Beispiel erstellen wir zwei Thread-Klassen, deren Aufgabe es ist, zwei zu akkumulieren Beginnend mit 1 werden Sie sofort denken, dass wir es verwenden, um eine Synchronisierung zu erreichen. Wie der Name schon sagt, wird es für die Überwachung der Werte verwendet der beiden Zähler im TwoCounter-Thread sind gleich. Es scheint, dass dies sinnlose Arbeit ist. Denn da sie synchron akkumuliert werden, wie können die Werte der beiden Zähler ungleich sein? 🎜>Aber das ist nicht der Fall. Bevor wir uns dieses Programm ansehen, schauen wir uns am besten Think in Java 14.2.1 an. Mein Programm ist tatsächlich anhand des in diesem Abschnitt angegebenen Beispiels vereinfacht . Die Hauptklasse wird in Sharing2 geändert
class TwoCounter erweitert Thread {
PRivate int count1 = 0, count2 = 0;
private boolean selected=false;
public void start(){
if (!started) file://Verhindern Sie mehrere Aufrufe der Start-Methode in einem Thread
{
started=true;
super.start();
}
}
public void run() {
while (true) {
count1++;
file ://Wenn TwoCounter bis zu diesem Punkt läuft und die CPU-Zeitscheibe dem Watcher zugewiesen wird, dann natürlich die Werte Die beiden von Watcher zu diesem Zeitpunkt gelesenen Zähler werden unterschiedlich sein. Diese Möglichkeit besteht. „Das liegt an der Natur von Threads – sie können jederzeit angehalten (pausiert) werden. Zwischen den Ausführungszeiten der beiden oben genannten Zeilen kommt es also manchmal zu einer Ausführungspause. Gleichzeitig wird die Ausführung des Watcher-Threads unterbrochen Folgen Sie auch, und der Vergleich wird zu diesem Zeitpunkt durchgeführt, was dazu führt, dass die Zähler ungleich sind "+count2) ;
try {
sleep(500);
} Catch (InterruptedException e){}
}
}
public void synchTest() {
Sharing2. incrementaccess();
if(count1 != count2)
System.out.println("Unsynced");//Sobald unsynchronisiert gefunden wird, wird es sofort angezeigt
}
}
class Watcher erweitert Thread {
private Sharing2 p;
public Watcher(Sharing2 p) {
this.p = p;
start();
}
public void run( ) {
while(true) {
p.s.synchTest();
try {
sleep(500);
} Catch (InterruptedException e){}
}
}
}
public class Sharing2 {
TwoCounter s;
private static int accessCount = 0;
public static void incrementAccess() {
accessCount++;
System. out.println("accessCount="+accessCount);
}
public static void main(String[] args) {
Sharing2 aaa = new Sharing2();
aaa.s=new TwoCounter ();
aaa.s.start();//TwoCounter-Thread öffnen
new Watcher(aaa);//Watcher-Thread öffnen
}
}
Kommentare oben Es wurde sehr deutlich erklärt, dass es zu einer Nichtsynchronisierung kommen kann. Das Seltsame ist jedoch, dass ich beim Ausführen nie auf eine Nichtsynchronisierung gestoßen bin , count1++ und count2++ im Programm können nicht gleichzeitig in den Watcher-Thread eingefügt werden, aber warum ist das Programm auf Think in Java nach der Ausführung definitiv nicht synchron? Der Unterschied besteht darin, dass mein Programm einfacher ist und dass die Ausführung unter der Befehlszeile ohne Verwendung der GUI teurer ist, sodass der Watcher davon profitieren kann Fügen Sie eine Schleifenanweisung zwischen count1++ und count2++ hinzu. Der Zweck der künstlichen Vergrößerung der Lücke besteht darin, dem Beobachter das Einfügen zu ermöglichen, was zu einer Situation führt, in der der überwachte count1 nicht gleich count2 ist, was zu einer Desynchronisation führt. Das geänderte Programm lautet wie folgt
......
count1++;
for(int i=0;i<5000;i++);
count2++;
......
OK! Führen Sie das Programm erneut aus und Sie werden es bald sehen. Das Phänomen der Unsynchronisierung ist aufgetreten, was zu bestätigen scheint, dass meine Analyse gerade richtig ist. Das Seltsame ist jedoch, dass es nach der einmaligen Ausgabe von Unsynchronized nie wieder aufgetreten ist , der Watcher-Thread hat nur einmal zwei Zähler erkannt. Ist es ein Zufall oder unvermeidlich, und es wird definitiv eine unsynchronisierte Ausgabe geben? Vergessen Sie es, lassen Sie uns dieses Problem erst einmal beiseite legen und weitermachen
Da das Problem der Nichtsynchronisierung aufgetreten ist, ist es offensichtlich, dass die Lösung synchronisiert ist: Ändern Sie die Ausführungsmethode und die SynchTest-Methode von TwoCounter. Was bedeutet das? Was sind die Vorteile? Abschnitt 14.2.2 enthält eine sehr detaillierte und ausführliche Erklärung. Insbesondere das Konzept der Monitore, das wir normalerweise als Objektsperren bezeichnen, wird im Buch sehr klar erläutert.
Kurz gesagt, der Code, der geändert werden muss folgt:
class TwoCounter erweitert Thread {
public synchronisiert void run() {
while (true) {
count1++;
count2++;
System.out.println("Count1= "+count1+",Count2="+count2);
versuchen Sie {
sleep(500);
} Catch (InterruptedException e){}
}
}
public synchronisiert void synchTest() {
Sharing2.incrementAccess();
if(count1 != count2)
System.out.println("Unsynched");//Sobald unsynchronisiert gefunden wird, wird es sein wird sofort angezeigt
}
}
Andere Dinge wegzulassen bedeutet, dass es tatsächlich sehr einfach ist, das Problem zu lösen, haha.
Wir stellen fest, dass sowohl run() als auch synchTest() „synchron“ sind " . Wenn nur eine der Methoden synchronisiert ist, kann die andere die Sperre des Objekts ignorieren und ungehindert aufgerufen werden. Sie müssen sich also eine wichtige Regel merken: Alle Methoden, die auf eine kritische gemeinsame Ressource zugreifen, müssen auf synchronisiert eingestellt sein, sonst funktionieren sie nicht ordnungsgemäß.
Jetzt bin ich auf ein neues Problem gestoßen. Watcher2 kann nie sehen, was vor sich geht, da die gesamte run()-Methode auf „synchron“ gesetzt ist. Und da run() für jedes Objekt ausgeführt werden muss, kann die Sperre nie geöffnet werden und synchTest() wird nie aufgerufen. Sie können dieses Ergebnis sehen, weil sich accessCount überhaupt nicht geändert hat.
Um dieses Problem zu lösen, besteht eine Möglichkeit darin, nur einen Teil des Codes in run() zu isolieren. Der Teil des Codes, den Sie auf diese Weise isolieren möchten, wird als „kritischer Bereich“ bezeichnet, und das synchronisierte Schlüsselwort muss auf unterschiedliche Weise verwendet werden, um einen kritischen Bereich festzulegen. Java bietet Unterstützung für kritische Bereiche durch „synchronisierte Blöcke“. Dieses Mal verwenden wir das synchronisierte Schlüsselwort, um anzugeben, dass die Sperre des Objekts verwendet wird, um den darin enthaltenen Code zu synchronisieren. Wie unten gezeigt:
synchronized(syncObject) {
// Auf diesen Code kann jeweils nur
// ein Thread gleichzeitig zugreifen, vorausgesetzt, alle
// Threads respektieren syncObject′s Sperre
}
Vor dem Betreten des synchronisierten Blocks muss die Sperre für das synchObject erhalten werden. Wenn ein anderer Thread die Sperre erworben hat, kann der Block nicht eintreten und muss warten, bis die Sperre aufgehoben wird.
Sie können das synchronisierte Schlüsselwort aus dem gesamten run() löschen und es durch einen synchronisierten Block ersetzen, der die beiden Schlüsselzeilen umgibt, wodurch die Änderung am Sharing2-Beispiel abgeschlossen wird. Doch welcher Gegenstand soll als Schloss dienen? Dieses Objekt wurde von synchTest() markiert? Das ist das aktuelle Objekt (dieses)! Die modifizierte run()-Methode sieht also so aus:
file:// Beachten Sie, dass es kein synchronisiertes Schlüsselwort gibt
public void run() {
while (true) {
synchronized ( this){
count1++;
count2++;
}
System.out.println("Count1="+count1+",Count2="+count2);
try {
Sleep (500);
} Catch (InterruptedException e){}
}
}
file://Hinweis: synchTest() benötigt immer noch das synchronisierte Schlüsselwort. Überlegen Sie, warum
In diesem Fall kann die synchTest-Methode aufgerufen werden, und wir können auch die Änderung in accessCount sehen
Das Obige ist der Inhalt des Memos (7) für Anfänger, die Java lernen, und weitere verwandte Inhalte Bitte beachten Sie die chinesische PHP-Website (m.sbmmt.com)!