Heim  >  Artikel  >  Java  >  Detaillierte Erklärung der Java-Synchronisierung

Detaillierte Erklärung der Java-Synchronisierung

高洛峰
高洛峰Original
2016-12-13 10:55:141252Durchsuche

Wenn Java-Sprachschlüsselwörter zum Ändern einer Methode oder eines Codeblocks verwendet werden, kann sichergestellt werden, dass höchstens ein Thread den Code gleichzeitig ausführt.

1. Wenn zwei gleichzeitige Threads auf den synchronisierten (diesen) Synchronisationscodeblock im selben Objektobjekt zugreifen, kann jeweils nur ein Thread ausgeführt werden. Ein anderer Thread muss warten, bis der aktuelle Thread die Ausführung dieses Codeblocks abgeschlossen hat, bevor er diesen Codeblock ausführen kann.

2. Wenn jedoch ein Thread auf einen synchronisierten (diesen) synchronisierten Codeblock des Objekts zugreift, kann ein anderer Thread immer noch auf den nicht synchronisierten (diesen) synchronisierten Codeblock im Objekt zugreifen.

3. Besonders kritisch ist, dass, wenn ein Thread auf einen synchronisierten (diesen) synchronisierten Codeblock des Objekts zugreift, andere Threads daran gehindert werden, auf alle anderen synchronisierten (diesen) synchronisierten Codeblöcke des Objekts zuzugreifen.

4. Das dritte Beispiel ist auch auf andere Synchronisationscodeblöcke anwendbar. Das heißt, wenn ein Thread auf einen synchronisierten (diesen) synchronisierten Codeblock eines Objekts zugreift, erhält er die Objektsperre dieses Objekts. Dadurch wird der Zugriff anderer Threads auf alle synchronisierten Codeteile des Objektobjekts vorübergehend blockiert.

5. Die oben genannten Regeln gelten auch für andere Objektsperren

Beispiel:
1. Wenn zwei gleichzeitige Threads auf den synchronisierten (diesen) Synchronisationscode im selben Objekt zugreifen Blockierung, es kann immer nur ein Thread gleichzeitig ausgeführt werden. Ein anderer Thread muss warten, bis der aktuelle Thread die Ausführung dieses Codeblocks abgeschlossen hat, bevor er diesen Codeblock ausführen kann.

Paket ths;

öffentliche Klasse Thread1 implementiert Runnable {
public void run() {
synchronisiert(this) {
for (int i = 0; i < ; 5; i++) {
                                                                                                                                                                                                                                      out ) ;
ta.start();
tb.start();
} 1
Eine synchronisierte Schleife 2
Eine synchronisierte Schleife 3
Eine synchronisierte Schleife 4
B synchronisiert Schleife 0
B synchronisierte Schleife 1
B synchronisierte Schleife 2
B synchronisierte Schleife 3
B synchronisierte Schleife 4

2. Wenn jedoch ein Thread auf eine synchronisierte (diese) zugreift, wird diese synchronisiert Codeblock des Objekts, ein anderer Thread kann immer noch auf den nicht synchronisierten (diesen) synchronisierten Codeblock im Objekt zugreifen.

Paket ths;

public class Thread2 {  
     public void m4t1() {  
          synchronisiert(this) {  
               int i = 5;  
               while( i-- > 0) {  
                    System.out.println(Thread.currentThread().getName() + " : " + i);  
                    try {  
                         Thread.sleep(500);  
                    } Catch (InterruptedException ie) {  
                    }       public void m4t2() {  
          int i = 5;  
          while( i-- > 0) {  
               System.out.println(Thread.currentThread().getName() + " : " + i);  
               try {  
                    Thread.sleep(500);            final Thread2 myt2 = new Thread2();  
          Thread t1 = new Thread(  new Runnable() {  public void run() {  myt2.m4t1();  }  }, "t1"  );  
          Thread t2 = new Thread(  new Runnable() {  public void run() { myt2.m4t2();   }  }, "t2"  );  
          t1.start();  
          t2.start();  
    t1 : 2  
     t2 : 2  
     t1 : 1  
     t2 : 1  
     t1 : 0  
     t2 : 0

    一个线程访问Objekt的一个synchronisiert(dies)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞.

#   
               while( i-- > 0) {  
                    System.out.println(Thread.currentThread().getName() + " : " + i);  
                    try {  
                         Thread.sleep(500);    }

结果:

     t1 : 4  
     t1 : 3  
     t1 : 2  
     t1 : 1  

     t1 : 0  

     t2 : 4  

     t2 : 3  

     t2 : 2  

     t2 : 1  

     t2 : 0

     四、第三个例子同样适用其它同步代码块.也就是说,当一个线程访问object的一个synchronized (dies)同步代码块时,它就获得了这个object的对象锁.结果其它线程对该object对象所有同步代码部分的访问都被暂时阻塞.

    . //修改Thread2.m4t2()方法如下:

     public synchronisiert void m4t2() {  
          int i = 5;  
          while( i-- > 0) {  
               System.out.println(Thread.currentThread().getName() + " : " + i);  

               try {  

                    Thread.sleep(500);   : 4  

     t1 : 3  

     t1 : 2  

     t1 : 1  

     t1 : 0  
     t2 : 4  
     t2 : 3  
     t2 : 2  
     t2 : 1  
    . t2 : 0

     五、以上规则对其它对象锁同样适用:

Paket ths;

public class Thread3 { 
     class Inner { 
          private void m4t1() { 
               int i = 5; 
               while(i-- > 0) { 
                    System.out.println(Thread.currentThread().getName() + " : Inner.m4t1()=" + i); 
                    try { 
                         Thread.sleep(500); 
                    } Catch(InterruptedException ie) { 
                   } 
         . } 
       private void m4t2() { 
               int i = 5; 
               while(i-- > 0) { 
                    System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i); 
                    try { 
                         Thread.sleep(500); } 
     private void m4t1(Inner inner) { 
          synchronisiert(inner) { //使用对象锁
          inner.m4t1(); 
     } 
     private void m4t2(Inner inner) { 
          inner.m4t2(); 
     } 
     public static void main(String[] args) { 
          final Thread3 myt3 = new Thread3(); 
          final Inner inner = myt3.new Inner(); 
          Thread t1 = new Thread( new Runnable() {public void run() { myt3.m4t1(inner);} }, "t1"); 
     Thread t2 = new Thread( new Runnable() {public void run() { myt3.m4t2(inner);} }, "t2"); 
     t1.start(); 
     t2.start(); 🎜一个Inner中的非同步部分.所以" : Inner.m4t2()=3  
     t1 : Inner.m4t1()=2  
     t2 : Inner.m4t2()=2  
     t1 : Inner.m4t1()=1  
     t2 : Inner .m4t2()=1  
     t1 : Inner.m4t1()=0  
     t2 : Inner.m4t2()=0

现在在Inner.m4t2()前面加上synchronized:

private synchronisiert void m4t2() {
int i = 5;
while(i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i);
try {
Thread.sleep(500);
} catch(InterruptedException ie)                                                            

Ergebnis:

Obwohl die Threads t1 und t2 auf zwei unabhängige Teile desselben Inner-Objekts zugegriffen haben, hat t2 keine Kontrolle über Inner, da t1 zuerst die Objektsperre für Inner erhalten hat. Der Zugriff auf .m4t2() ist ebenfalls blockiert, da m4t2(). ist eine synchronisierte Methode in Inner.

t1 : Inner.m4t1()=4

t1 : Inner.m4t1()=3

t1 : Inner.m4t1()=2

t1 : Inner.m4t1()= 1
t1 : Inner.m4t1()=0
t2 : Inner.m4t2()=4
t2 : Inner.m4t2()=3
t2 : Inner.m4t2()=2
t2 : Inner.m4t2()=1
t2 : Inner.m4t2()=0

Teil 2:

synchronisiertes Schlüsselwort, das zwei Verwendungen enthält: synchronisierte Methoden und synchronisiert Blöcke.

1. synchronisierte Methode: Deklarieren Sie die synchronisierte Methode, indem Sie das synchronisierte Schlüsselwort zur Methodendeklaration hinzufügen. Beispiel:

public synchronisiert void accessVal(int newVal);

Die synchronisierte Methode steuert den Zugriff auf Klassenmitgliedsvariablen: Jede Klasseninstanz entspricht einer Sperre, und jede synchronisierte Methode muss Zugriff auf die aufrufende Klasseninstanz erhalten Die Sperre kann

ausgeführt werden, andernfalls wird der Thread, zu dem sie gehört, blockiert. Sobald die Methode ausgeführt wird, wird die Sperre erst bei der Rückkehr von der Methode freigegeben. Danach kann der blockierte Thread die Sperre erhalten und erneut in den Ausführungsstatus

wechseln. Dieser Mechanismus stellt sicher, dass sich für jede Klasseninstanz gleichzeitig höchstens eine ihrer als synchronisiert deklarierten Mitgliedsfunktionen im ausführbaren Zustand befindet (da höchstens nur

die der Klasseninstanz entsprechende Sperre erhalten kann). , Dies vermeidet effektiv Zugriffskonflikte von Klassenmitgliedsvariablen (solange alle Methoden, die auf Klassenmitgliedsvariablen zugreifen können, als synchronisiert deklariert sind)

.

In Java entspricht nicht nur Klasseninstanzen jeder Klasse eine Sperre. Auf diese Weise können wir auch die statischen Mitgliedsfunktionen der Klasse als synchronisiert deklarieren, um ihre statischen Mitglieder der Klasse zu steuern

Variabler Zugriff. Mängel der
synchronisierten Methode: Wenn eine große Methode als synchronisiert deklariert wird, wirkt sich dies normalerweise stark auf die Effizienz aus, wenn die Thread-Klassenmethode run() aufgrund der Gesamtheit als synchronisiert deklariert wird Thread Es wird während seiner Lebensdauer immer ausgeführt, sodass der Aufruf einer synchronisierten Methode dieser Klasse niemals erfolgreich sein wird. Natürlich können wir

dieses Problem lösen, indem wir den Code, der auf Klassenmitgliedsvariablen zugreift, in eine spezielle Methode einfügen, ihn als synchronisiert deklarieren und ihn in der Hauptmethode aufrufen, aber Java stellt uns

zur Verfügung Eine bessere Lösung ist der synchronisierte Block.

2. Synchronisierter Block: Deklarieren Sie den synchronisierten Block über das synchronisierte Schlüsselwort. Die Syntax lautet wie folgt:

synchronized(syncObject) {

//Code, der die Zugriffskontrolle ermöglicht

}

synchronized block ist ein Codeblock, in dem der Code das Objekt syncObject erhalten muss (wie bereits erwähnt). , Sie können die Sperre nur ausführen, wenn es sich um eine Klasseninstanz oder eine Klasse handelt. Der spezifische Mechanismus

ist der gleiche wie oben erwähnt. Da es auf jeden Codeblock abzielen und das gesperrte Objekt beliebig angeben kann, verfügt es über eine hohe Flexibilität.
Einige Verständnisse von synchronisiert(this)
1. Wenn zwei gleichzeitige Threads auf den synchronisierten(this)-Synchronisationscodeblock im selben Objektobjekt zugreifen, kann jeweils nur ein Thread ausgeführt werden. Ein anderer Thread

muss warten, bis der aktuelle Thread die Ausführung dieses Codeblocks abgeschlossen hat, bevor er diesen Codeblock ausführen kann.

2. Wenn jedoch ein Thread auf einen synchronisierten (diesen) synchronisierten Codeblock des Objekts zugreift, kann ein anderer Thread immer noch auf den nicht synchronisierten (diesen) synchronisierten Codeblock im Objekt zugreifen.
3. Was besonders kritisch ist, ist, dass, wenn ein Thread auf einen synchronisierten (diesen) Synchronisationscodeblock eines Objekts zugreift, andere Threads auf alle anderen synchronisierten (diesen)
-Objekte zugreifen.

Der Zugriff auf synchronisierte Codeblöcke wird blockiert.
4. Das dritte Beispiel ist auch auf andere synchronisierte Codeblöcke anwendbar. Das heißt, wenn ein Thread auf einen synchronisierten (diesen) synchronisierten Codeblock eines Objekts zugreift, erhält er die Objektsperre dieses

Objekts. Dadurch wird der Zugriff anderer Threads auf alle synchronisierten Codeteile des Objektobjekts vorübergehend blockiert.
5. Die oben genannten Regeln gelten auch für andere Objektsperren

http://hi.baidu.com/sunshibing/blog/item/5235b9b731d48ff430add14a.html
Verwendung von synchronisiert in Java

Zum Beispiel: Ein Objekt ist wie ein großes Haus, die Tür ist immer offen. Es gibt viele Räume (also Methoden) im Haus.

Diese Räume sind entweder verschlossen (synchronisierte Methode) oder unverschlossen (normale Methode). An der Zimmertür befindet sich ein Schlüssel. Mit diesem Schlüssel können alle verschlossenen Räume geöffnet werden.

Außerdem vergleiche ich alle Threads, die die Methoden des Objekts aufrufen wollen, mit Leuten, die einen bestimmten Raum in diesem Haus betreten wollen. Das ist alles. Werfen wir einen Blick darauf, wie diese Dinge miteinander funktionieren.

Hier klären wir zunächst unsere Voraussetzungen. Das Objekt verfügt über mindestens eine synchronisierte Methode, ansonsten hat dieser Schlüssel keine Bedeutung. Natürlich wird es bei uns kein solches Thema geben.

Eine Person wollte einen verschlossenen Raum betreten. Er kam zur Tür des Hauses und sah dort den Schlüssel (was darauf hindeutet, dass noch niemand den verschlossenen Raum benutzen wollte). Also ging er hin, holte sich den Schlüssel

und nutzte die Räume nach seinem Plan. Bitte beachten Sie, dass er den Schlüssel nach jeder Nutzung des verschlossenen Zimmers unverzüglich zurückgibt. Auch wenn er zwei verschlossene Räume hintereinander nutzen möchte,

muss er den Schlüssel zurückgeben und ihn in der Mitte wieder holen.

Daher gilt bei der Verwendung von Schlüsseln unter normalen Umständen: „Ausleihen, sobald Sie sie verwenden, zurückgeben, sobald Sie sie verwenden.“

Zu diesem Zeitpunkt können andere die entsperrten Schlüssel auch ohne verwenden Beschränkung: Eine Person kann ein Zimmer nutzen, oder zwei Personen können ein Zimmer nutzen, es gibt keine Begrenzung. Aber wenn jemand einen verschlossenen Raum betreten will, muss er zur Tür laufen, um ihn zu sehen. Wenn Sie den Schlüssel haben, können Sie ihn natürlich mitnehmen und gehen. Wenn Sie den Schlüssel nicht haben, müssen Sie warten.

Wenn viele Leute auf diesen Schlüssel warten, wer bekommt den Schlüssel zuerst, wenn er zurückgegeben wird? Nicht garantiert. Wie der Typ im vorherigen Beispiel, der zwei verschlossene Räume hintereinander nutzen möchte: Wenn andere Personen auf den Schlüssel warten,

er den Schlüssel zurückgibt, gibt es keine Garantie dafür, dass dieser Typ ihn wieder bekommen kann . (In der JAVA-Spezifikation heißt es an vielen Stellen eindeutig, dass es keine Garantie gibt, z. B. wie lange es dauern wird, bis

Thread.sleep() nach einer Pause wieder läuft. Welcher Thread mit der gleichen Priorität wird sein? Wird zuerst ausgeführt, wenn die Sperre für den Zugriff auf das Objekt aufgehoben wird. Welcher der mehreren Threads im Wartepool erhält zuerst

usw. Ich denke, die endgültige Entscheidung liegt bei der JVM. Der Grund, warum es keine Garantie gibt Denn wenn die JVM die oben genannte Entscheidung trifft, ist sie nie die richtige. Sie basiert lediglich auf einer Bedingung, sondern

, da es zu viele Beurteilungsbedingungen gibt Es wird angegeben, dass dies möglicherweise Auswirkungen auf die Förderung von JAVA hat oder auf den Schutz des geistigen Eigentums zurückzuführen ist. Ich glaube jedoch, dass diese Unsicherheiten nicht völlig ungewiss sind selbst funktioniert nach Anweisungen, tatsächlich gibt es Regeln, die gefunden werden können

Jeder, der sich mit Computern beschäftigt hat, weiß, dass der wissenschaftliche Name von Zufallszahlen in Computern Pseudozufallszahlen ist, die von Menschen geschrieben werden mit einer bestimmten Methode. Vielleicht liegt es auch daran, dass es zu mühsam und bedeutungslos ist, es zu bestimmen.

Wenn Sie sich also nicht sicher sind, sind Sie es nicht.)

Werfen wir einen Blick darauf am Synchronisationscodeblock. Es unterscheidet sich geringfügig von der Synchronisationsmethode.

1. Von der Größe her sind synchronisierte Codeblöcke kleiner als synchronisierte Methoden. Sie können sich einen synchronisierten Codeblock als einen Raum in einem unverschlossenen Raum vorstellen, der durch einen gesperrten Bildschirm getrennt ist.

2. Der Synchronisationscodeblock kann auch den Schlüssel eines anderen Objekts künstlich angeben. Genauso wie Sie angeben, mit welchem ​​Schlüssel das Schloss dieses Bildschirms geöffnet werden kann, können Sie auch den Schlüssel dieses Hauses verwenden

, um es mit dem Schlüssel eines anderen Hauses zu öffnen. Sie müssen rennen, um den Schlüssel aus dem anderen Haus zu holen, und mit dem Schlüssel dieses Hauses den gesperrten Bildschirm dieses Hauses öffnen.

Denken Sie daran, dass der Schlüssel zu dem anderen Haus, den Sie erhalten haben, andere nicht daran hindert, den unverschlossenen Raum in diesem Haus zu betreten.

Warum synchronisierte Codeblöcke verwenden? Ich denke, es sollte so sein: Zunächst wirkt sich der Synchronisierungsteil des Programms auf die Betriebseffizienz aus, und eine Methode besteht normalerweise darin, zuerst einige lokale Variablen

zu erstellen und dann einige Operationen an diesen Variablen durchzuführen. B. Berechnungen, Anzeige usw. Je mehr Code durch die Synchronisierung abgedeckt wird, desto schwerwiegender sind die Auswirkungen auf die Effizienz. Deshalb versuchen wir normalerweise, den Umfang der Auswirkungen so gering wie möglich zu halten.

Wie geht das? Synchronisierte Codeblöcke. Wir synchronisieren nur die Teile einer Methode, die synchronisiert werden sollen, beispielsweise Operationen.

Darüber hinaus kann der Synchronisationscodeblock den Schlüssel angeben. Diese Funktion hat den zusätzlichen Vorteil, dass der Schlüssel eines Objekts innerhalb eines bestimmten Zeitraums belegt wird. Erinnern Sie sich noch an die Prinzipien der Tastenbenutzung

unter normalen Umständen? Dies sind keine gewöhnlichen Umstände. Der von Ihnen erhaltene Schlüssel wird nicht für immer zurückgegeben, sondern beim Verlassen des synchronisierten Codeblocks.

Verwenden Sie auch die Analogie des Mannes vorhin, der zwei verschlossene Räume hintereinander nutzen wollte. Wie kann ich nach der Nutzung eines Raumes weiterhin einen anderen Raum nutzen? Verwenden Sie synchronisierte Codeblöcke. Erstellen Sie zunächst einen weiteren

-Thread, erstellen Sie einen Synchronisierungscodeblock und richten Sie das Schloss dieses Codeblocks auf den Schlüssel des Hauses. Dann starte diesen Thread. Solange Sie den Hausschlüssel

beim Betreten dieses Codeblocks erhalten, können Sie ihn behalten, bis Sie diesen Codeblock verlassen. Mit anderen Worten, Sie können sogar alle verschlossenen Räume in diesem Raum durchqueren und sogar schlafen (10*60*1000), aber es warten immer noch

1000 Threads auf den Schlüssel an der Tür. Es macht ziemlich viel Spaß.

Lassen Sie uns über die Korrelation zwischen der Methode „sleep()“ und dem Schlüssel sprechen. Wenn ein Thread nach Erhalt des Schlüssels zum Sleep() gezwungen wird und den Synchronisierungsinhalt nicht abgeschlossen hat, ist der Schlüssel immer noch vorhanden

. Der Schlüssel wird erst zurückgegeben, wenn er erneut ausgeführt wird und der gesamte Synchronisierungsinhalt abgeschlossen ist. Denken Sie daran, der Typ war einfach müde von der Arbeit und ging, um sich auszuruhen. Er hat das, was er tun wollte, nicht zu Ende gebracht. Um

zu verhindern, dass andere diesen Raum betreten und darin Chaos anrichten, musste er den einzigen Schlüssel auch im Schlaf tragen.

Abschließend fragen sich manche vielleicht: Warum brauchen wir einen Schlüssel zum Öffnen der Tür und nicht einen Schlüssel für jede Tür? Ich denke, das ist eine reine Frage der Komplexität. Ein Schlüssel für eine Tür ist sicherlich

sicherer, bringt aber viele Probleme mit sich. Die Erstellung, Aufbewahrung, Beschaffung, Rückgabe von Schlüsseln etc. Seine Komplexität kann mit zunehmenden Synchronisationsmethoden geometrisch zunehmen, was die Effizienz erheblich beeinträchtigt. Dies ist

eine Frage des Kompromisses. Wie unerwünscht ist es, die Effizienz stark zu reduzieren, um die Sicherheit ein wenig zu erhöhen.

Ein einfaches Beispiel für eine synchronisierte

öffentliche Klasse TextThread {

public static void main(String[] args) {
TxtThread tt = new TxtThread( new Thread(tt).start();
new Thread(tt).start();
new Thread(tt).start(); ;
}
}

class TxtThread implementiert Runnable {

int num = 100;

String str = new String();

public void run() {

synchronisiert (str) {

while (num > 0) {

try {

Thread.sleep(1);

} Catch (Exception e) {
e.getMessage();
}
System.out.println(Thread.currentThread().getName()
+ "this is" + num--); }
}
}

Um im obigen Beispiel einen Zeitunterschied zu erzeugen, der die Möglichkeit für Fehler bietet, wird Thread.sleep(10)

Java unterstützt Multithreading wird verwendet Der Synchronisierungsmechanismus wird von allen geliebt. Es scheint, dass die Verwendung des synchronisierten Schlüsselworts das Problem der Multithread-Synchronisierung gemeinsamer Daten leicht lösen kann. Was genau ist

? ——Bevor wir eine Schlussfolgerung ziehen können, müssen wir die Rolle des synchronisierten Schlüsselworts genau verstehen.

Im Allgemeinen kann das synchronisierte Schlüsselwort als Modifikator einer Funktion oder als Anweisung innerhalb einer Funktion verwendet werden, die normalerweise als Synchronisationsmethode und Synchronisationsanweisungsblock bezeichnet wird. Bei weiterer Klassifizierung kann

synchronized auf Instanzvariablen, Objektreferenzen (Objektreferenzen), statische Funktionen und Klassenliterale (Klassennamenliterale) wirken.

Bevor wir weiter darauf eingehen, müssen wir einige Punkte klären:

A. Unabhängig davon, ob das synchronisierte Schlüsselwort einer Methode oder einem Objekt hinzugefügt wird, handelt es sich bei der Sperre, die es erhält, um ein Objekt, anstatt einen Codeabschnitt oder eine Funktion als Sperre zu behandeln – und die synchronisierte Methode wird wahrscheinlich von anderen Threads blockiert. Objektzugriff.

B. Jedem Objekt ist nur eine Sperre zugeordnet.

C. Das Erreichen einer Synchronisierung erfordert einen hohen Systemaufwand und kann sogar zu einem Deadlock führen. Vermeiden Sie daher unnötige Synchronisierungskontrollen.

Als Nächstes diskutieren wir die Auswirkungen der Synchronisierung an verschiedenen Stellen auf den Code:

Angenommen, P1 und P2 sind unterschiedliche Objekte derselben Klasse. Diese Klasse definiert Synchronisierungsblöcke für die folgenden Situationen: Oder Synchronisationsmethode, P1 und P2 können sie aufrufen.

1. Bei Verwendung von synchronisiert als Funktionsmodifikator lautet der Beispielcode wie folgt:

Public synchronisiert void methodAAA()

{

//….

}

Dies ist die Synchronisationsmethode. Welches Objekt ist zu diesem Zeitpunkt synchronisiert? Was gesperrt wird, ist das Objekt, das diese synchronisierte Methode aufruft. Mit anderen Worten: Wenn ein Objekt P1 diese Synchronisationsmethode in verschiedenen Threads

ausführt, wird ein gegenseitiger Ausschluss zwischen ihnen gebildet, um einen Synchronisationseffekt zu erzielen. Allerdings kann ein anderes Objekt P2, das von der Klasse generiert wird, zu der dieses Objekt gehört, diese Methode mit dem hinzugefügten Schlüsselwort

synchronized willkürlich aufrufen.

Der obige Beispielcode entspricht dem folgenden Code:

public void methodAAA()

{

synchronized (this) // (1)

{

//…..

}

}

Worauf bezieht sich das in (1)? Es bezieht sich auf das Objekt, das diese Methode aufruft, z. B. P1. Es ist ersichtlich, dass der Kern der Synchronisationsmethode darin besteht, die Synchronisation auf die Objektreferenz anzuwenden. ——Nur der Thread, der die P1-Objektsperre erhalten hat, kann die Synchronisationsmethode von P2 aufrufen. Die P1-Sperre hat damit möglicherweise auch nichts zu tun Die Synchronisierung führt in dieser Situation dazu, dass

Datenchaos verursacht: (

2. Synchronisierungsblock, der Beispielcode lautet wie folgt:

public void method3 (SomeObject so)

{

synchronisiert(so)

{

//…..

}

}

Zu diesem Zeitpunkt ist die Sperre ein Objekt. Wer die Sperre erhält, kann den von ihr gesteuerten Code ausführen. Wenn es ein klares Objekt als Sperre gibt, können Sie das Programm wie folgt schreiben, aber wenn es kein klares Objekt als gibt Wenn Sie nur einen Codeabschnitt synchronisieren möchten, können Sie eine spezielle Instanzvariable (es muss ein Objekt sein) erstellen, die als Sperre fungiert:

Klasse Foo implementiert Runnable

{

private byte[] lock = new byte[0]; // Spezielle Instanzvariable

Public void methodA()

{

synchronisiert(lock) { //… }


}

Bytecode: Es sind nur 3 Opcodes erforderlich, um ein Byte[]-Objekt mit der Länge Null zu generieren, während Objektsperre

= new Object() erfordert 7 Zeilen Opcodes. Der Beispielcode lautet wie folgt:

Class Foo

{

public synchronisiert static void methodAAA() // Synchronized static function

{

/ … )

}

}

Die methodBBB()-Methode im Code verwendet das Klassenliteral als Sperre. Sie hat den gleichen Effekt wie die synchronisierte statische Funktion. Die erhaltene Sperre ist etwas ganz Besonderes . Es ist der aktuelle Aufruf dazu
}

🎜>
Die Klasse, zu der das Objekt der Methode gehört (Klasse, und nicht ein bestimmtes von dieser Klasse generiertes Objekt).

Ich erinnere mich, dass ich im Buch „Effective Java“ gelesen habe, dass die Verwendung von Foo.class und P1.getClass() für Synchronisationssperren unterschiedlich ist. Sie können P1.getClass() nicht zum Sperren dieser Klasse verwenden 🎜>Zweck. P1 bezieht sich auf das von der Foo-Klasse generierte Objekt.

Daraus kann geschlossen werden: Wenn eine synchronisierte statische Funktion A in einer Klasse definiert ist und auch eine synchronisierte Instanzfunktion B definiert ist, wird dasselbe Objekt Obj

dieser Klasse gespeichert Beim Zugriff auf die beiden Methoden A und B erfolgt keine Synchronisierung, da ihre Sperren unterschiedlich sind. Die Sperre von Methode A ist das Objekt Obj, und die Sperre von Methode B ist die Klasse, zu der Obj gehört.

Die Zusammenfassung lautet wie folgt:


Wenn wir wissen, welches Objekt durch die Synchronisierung gesperrt ist, können wir sicherere Multithread-Programme entwerfen.

Es gibt auch einige Techniken, die unseren synchronen Zugriff auf gemeinsam genutzte Ressourcen sicherer machen können:

1. Definieren Sie die private Instanzvariable + ihre Get-Methode anstelle der öffentlichen/geschützten Instanzvariablen. Wenn die Variable als öffentlich definiert ist, kann das Objekt

die Steuerung der Synchronisationsmethode umgehen und sie direkt von der Außenwelt beziehen und ändern. Dies ist auch eine der Standardimplementierungsmethoden von JavaBean.

2. Wenn es sich bei der Instanzvariablen um ein Objekt wie ein Array oder eine ArrayList handelt, ist die obige Methode immer noch unsicher, da das externe Objekt, wenn es über die get-Methode die Referenz des Instanzobjekts

erhält, darauf verweist ein anderes Objekt, dann wird sich diese private Variable auch ändern, wäre das nicht sehr gefährlich? Zu diesem Zeitpunkt müssen Sie der get-Methode und

eine synchronisierte Synchronisierung hinzufügen und nur den clone() dieses privaten Objekts zurückgeben. Auf diese Weise erhält der Aufrufer einen Verweis auf die Objektkopie

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Vorheriger Artikel:Java-Socket-ProgrammierungNächster Artikel:Java-Socket-Programmierung