2016-06-30 48 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+" "); 
     } 
    } 
} 

Qui ho sincronizzato sulla variabile ab. E ho anche creato due diverse istanze di controllo di classe, ma ottengo sempre output per s1 seguito da s2 o viceversa, ma non misto, perché è così? quando ho già creato due oggetti separati (in main), quindi due thread differenti, due diverse variabili ab, quindi come diventa una risorsa condivisa per i due diversi oggetti?Sincronizzazione thread su variabile istanza Integer

+5

B.T.W., non si sta sincronizzando sulla _variabile_, si sta eseguendo la sincronizzazione sull'oggetto a cui la variabile fa riferimento. È una distinzione importante perché alcune persone hanno commesso l'errore di non rendersi conto che la variabile può riferirsi a oggetti diversi in momenti diversi, e alcuni hanno commesso l'errore di non rendersi conto che lo stesso oggetto può essere referenziato da più di una variabile. –

+0

Questo codice è più simile a un codice di prova che a un caso reale, ma se si desidera eseguire la sincronizzazione su un [numero] lock-free, ma sicuro per i thread (https://docs.oracle.com/javase/8/ docs/api/java/lang/Number.html), puoi usare un [AtomicInteger] (https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicInteger.html). –

+0

@jameslarge: Capisco, cosa stai dicendo. Non ero preciso su questo perché il mio problema si concentrava su un altro punto. Comunque, grazie. :) – Santanu

risposta

12

TL; DR - è a causa di Integer pooling. Effettuare un Object (ad esempio Object ab = new Object()) per garantire che ogni istanza del blocco di Check non interferisca con gli altri.


Inizialmente ero perplesso. È interessante notare che, se si cambia

private Integer ab = 2; 

a

private Object ab = new Object(); 

la sincronizzazione va via (si ottiene diverse uscite su ogni corsa). Indietro con ab come Integer, ho eseguito il codice in modalità di debug (con un punto di interruzione sulla riga del nome del thread di stampa) e ho trovato quanto segue. Ecco il primo thread:

First thread variables

Ed ecco il secondo thread.

Second thread variables

Si noti che in realtà è lo stesso oggetto, [email protected]. Anche se hai pensato di ottenere due oggetti diversi, entrambi i campi ab nelle due istanze di Verifica si riferiscono allo stesso oggetto in memoria! Quindi sì, c'è la sincronizzazione corretta. La domanda, quindi, è come dicendo che lo Integer ab = 2 riceve due volte lo stesso oggetto Integer in memoria.


Dicendo Integer ab = 2, si utilizza autoboxing, con cui un valore di base (di tipo int) viene convertito automaticamente nel corrispondente oggetto Integer. Ciò equivale a chiamare il metodo autoboxing chiamata:

private Integer ab = Integer.valueOf(2); 

Se guardiamo in Integer.valueOf, notiamo che ha una piscina per i valori in un determinato intervallo:

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

Per la maggior parte delle impostazioni convenzionali, questo includerà il valore 2. Quindi stai ricevendo lo stesso valore Integer quando chiami questo metodo.

+0

Davvero interessante. Molte grazie. :) – Santanu

+2

Il * "certo intervallo" * è da -128 a 127 per impostazione predefinita. Si noti che IntegerCache.high può essere modificato. IntegerCache.low non può. –

1

Se si dà un'occhiata a bytecode si sarebbe probabilmente vedere questo codice:

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 cerca di box il valore di base 2 di opporsi chiamando #valueOf di Integer di classe. E come sappiamo, lo valueOf degli Integer implementa il modello Flyweight. Pertanto, lo stesso oggetto è condiviso tra le istanze.

4

Questo sta accadendo a causa del concetto Ingeter Pool in Java.

I numeri interi compresi tra -128 e 127 (inclusi) vengono utilizzati allo stesso modo del pool di stringhe.

Pertanto, quando si utilizza private Integer ab = 2;, ab viene condiviso per entrambi gli oggetti di Check.

È possibile utilizzare il valore> 128 o qualsiasi altro tipo di oggetto in modo che il codice non venga sincronizzato.

È possibile visualizzare le risposte qui: Why does the behavior of the Integer constant pool change at 127? per comprendere il concetto di pool intero.

+2

Se sono necessari oggetti esplicitamente distinti, è necessario crearli esplicitamente. L'uso di numeri interi più grandi ti darà oggetti distinti nelle attuali implementazioni, ma non è garantito per farlo. – plugwash