2016-06-30 15 views
11
public class Main{ 
    public static void main(String[] args) throws Exception { 
     // Creating objects for class Check(2 different objects) 
     Check c = new Check("s1"); 
     Check c1 = new Check("s2"); 
     c.start();c1.start(); 
    } 
} 
class Check extends Thread{ 
    Check(String name){super(name);} 
    private Integer ab = 2; 
    public void run(){ 
     synchronized (ab) { 
      System.out.println(Thread.currentThread().getName()); 
      for(int i=0;i<10;i++)System.out.print(i+" "); 
     } 
    } 
} 

Hier habe ich auf die Variable ab synchronisiert. Und ich habe auch zwei verschiedene Instanzen der Klassenüberprüfung erstellt, aber ich bekomme immer Ausgaben für s1 gefolgt von s2 oder umgekehrt, aber nicht gemischt, warum ist das so? wenn ich bereits zwei separate Objekte (in main) erstellt habe, also zwei verschiedene Threads, zwei verschiedene ab-Variablen, also wie es zu einer gemeinsamen Ressource für die zwei verschiedenen Objekte wird?Thread-Synchronisation auf Integer-Instanzvariable

+5

B.T.W., Sie synchronisieren nicht auf der _variable_, Sie synchronisieren auf das Objekt, auf das sich die Variable bezieht. Es ist ein wichtiger Unterschied, weil einige Leute den Fehler gemacht haben, nicht zu erkennen, dass sich Variable zu verschiedenen Zeiten auf verschiedene Objekte beziehen kann, und einige haben den Fehler gemacht, nicht zu erkennen, dass dasselbe Objekt von mehr als einer Variablen referenziert werden kann. –

+0

Dieser Code sieht eher wie ein Testcode als ein echter Fall aus, aber wenn Sie auf einem gesperrten, aber threadsicheren System synchronisieren möchten, [Number] (https://docs.oracle.com/javase/8/ docs/api/java/lang/Number.html) können Sie einen [AtomicInteger] (https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicInteger.html) verwenden). –

+0

@jameslarge: Ich verstehe, was du sagst. Ich war nicht genau darauf, weil mein Problem sich auf einen anderen Punkt konzentrierte. Wie auch immer, danke. :) – Santanu

Antwort

12

TL; DR - es ist wegen Integer Pooling. Machen Sie ab Object (d. H. Object ab = new Object()), um zu garantieren, dass jede Instanz der Check Verriegelung nicht die anderen beeinträchtigt.


Ich war zunächst auch verwirrt. Interessanterweise, wenn Sie

ändern
private Integer ab = 2; 

zu

private Object ab = new Object(); 

die Synchronisation geht weg (Sie können verschiedene Ausgänge auf jedem Lauf erhalten). Zurück mit ab als Integer, lief ich Ihren Code im Debug-Modus (mit einem Haltepunkt in der Druck-Thread-Name-Zeile) und Folgendes gefunden. Hier ist der erste Thread:

First thread variables

Und hier ist der zweite Thread.

Second thread variables

Hinweis, dass es eigentlich das gleiche Objekt, [email protected]. Obwohl Sie dachten, dass Sie zwei verschiedene Objekte erhalten, verweisen beide Felder in den beiden Check-Instanzen auf das gleiche Objekt im Speicher! Also ja, es gibt korrekte Synchronisation. Die Frage ist also, wie das Sprechen Integer ab = 2 zweimal das gleiche Integer Objekt im Speicher bekommt.


von Integer ab = 2 sagen, verwenden Sie Autoboxing, durch die ein Grundwert (vom Typ int) Integer automatisch in den entsprechenden Objekttyp umgewandelt wird. Dies entspricht der Autoboxing Methodenaufruf Aufruf:

private Integer ab = Integer.valueOf(2); 

Wenn wir in Integer.valueOf betrachten, stellen wir fest, dass es einen Pool für Werte in einem bestimmten Bereich hat:

public static Integer valueOf(int i) { 
    if (i >= IntegerCache.low && i <= IntegerCache.high) 
     return IntegerCache.cache[i + (-IntegerCache.low)]; 
    return new Integer(i); 
} 

Für die meisten herkömmlichen Einstellungen, diese enthält den Wert 2. Somit erhalten Sie den gleichen Integer Wert im Speicher, wenn Sie diese Methode aufrufen.

+0

Wirklich interessant. Danke vielmals. :) – Santanu

+2

Der * "bestimmte Bereich" * ist standardmäßig von -128 bis 127. Beachten Sie, dass IntegerCache.high geändert werden kann. IntegerCache.low kann nicht. –

1

Wenn Sie einen Blick auf Bytecode nehmen würden Sie wahrscheinlich diesen Code sehen:

LINENUMBER 15 L1 
ALOAD 0 
ICONST_2 
INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; 
PUTFIELD com/example/Check.ab : Ljava/lang/Integer; 

Java versucht, den Grundwert Feld 2 zum Objekt von #valueOf von Integer Klasse aufrufen. Und wie wir wissen, IntegervalueOf implementiert Muster. Somit wird dasselbe Objekt zwischen Instanzen geteilt.

4

Dies geschieht wegen Ingeter Pool Konzept in Java.

Ganzzahlbereiche zwischen -128 und 127 (einschließlich) werden auf dieselbe Weise wie String-Pool verwendet.

Also, wenn Sie private Integer ab = 2; verwenden, wird AB für beide Objekte von Check freigegeben.

Sie können Wert> 128 oder einen anderen Objekttyp verwenden, damit der Code nicht synchronisiert wird.

Sie können Antworten hier betrachten: Why does the behavior of the Integer constant pool change at 127? für das Verständnis von Integer Pool-Konzept.

+2

Wenn Sie explizit eindeutige Objekte benötigen, sollten Sie diese explizit erstellen. Wenn Sie größere Ganzzahlen verwenden, erhalten Sie in den aktuellen Implementierungen unterschiedliche Objekte, aber dies ist nicht gewährleistet. – plugwash

Verwandte Themen