2013-04-26 12 views
7
private volatile Object obj = new MyObject(); 
void foo() 
{ 
    synchronized(obj) 
    { 
    obj.doWork(); 
    } 
} 
void bar() 
{ 
    synchronized(obj) 
    { 
    obj.doWork(); 
    obj = new MyObject(); // <<<< notice this line (call it line-x) 
    } 
} 

supponga ad un certo punto nel tempo, un filo t_bar sta eseguendo bar(), e un'altra t_foo sta eseguendo foo e che t_bar ha appena acquisito obj, così t_foo è, in effetti, in attesa.sincronizzare sull'oggetto non finale

Dopo l'esecuzione del blocco di sincronizzazione in bar, foo eseguirà il blocco di sincronizzazione, giusto? Quale valore di obj vedrebbe? Quello vecchio? O il nuovo set in bar?

(vorrei speranza che il nuovo valore è visto, questo è il punto di codifica in questo modo, ma voglio sapere se questa è una scommessa 'sicuro')

+3

@javapirate: trovo MOLTO scortese che hai modificato il mio post semplicemente perché preferisci il tuo stile di formattazione K & R? Mi dispiace, ma dovrò riformattare di nuovo. –

+0

Siamo spiacenti. Ho pensato di liberarmi di molti spazi vuoti. Vai avanti! –

+0

Una distinzione che potrebbe aiutare a capire è che * oggetti * o non finale o non finale, sono le variabili che contengono riferimenti a loro. Il lucchetto si trova sull'oggetto, non sulla variabile. –

risposta

0

Questo è pericoloso e rotto . Cambiare l'oggetto che si blocca non funziona.

Quando un thread tenta di immettere un blocco sincronizzato, deve prima valutare l'espressione in paren per capire quale blocco ha bisogno. Se il blocco cambia dopo, il thread non ha alcun modo di saperlo, alla fine acquisisce il vecchio blocco ed entra nel blocco sincronizzato. A quel punto vede l'oggetto e lo valuta, ottenendo il nuovo riferimento, e chiama il metodo su di esso con il vecchio (ora irrilevante) blocco e senza tenere il nuovo blocco, anche se qualche altro thread potrebbe avere il nuovo blocco trattenuto e potrebbe eseguire il metodo sullo stesso oggetto contemporaneamente.

1

Si comporterà normalmente come se il riferimento dell'oggetto non fosse stato modificato internamente. Il motivo per cui il test per il blocco su oggetto verrà eseguito una sola volta. Quindi anche se l'oggetto cambia internamente, il thread continuerà ad attendere e il comportamento si ripeterà come se l'oggetto fosse lo stesso [invariato].

Ho provato un'altra cosa. Ho inserito un'istruzione sleep subito dopo la creazione di un nuovo oggetto, quindi ho avviato il thread successivo e, come previsto, entrambi i thread hanno iniziato a funzionare contemporaneamente. Vedere il codice qui sotto.

public class ChangeLockObjectState { 

    private volatile Object obj = new Object(); 

    void foo() { 
     synchronized (obj) { 
      try { 
       System.out.println("inside foo"); 
       Thread.sleep(10000); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
    } 

    void bar() { 
     synchronized (obj) { 
      try { 
       System.out.println("inside bar"); 
       Thread.sleep(5000); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 

      obj = new Object(); // <<<< notice this line (call it line-x) 

      System.out.println("going out of bar"); 

      try { 

       Thread.sleep(5000); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 

      System.out.println("wait over"); 

     } 
    } 

    /** 
    * @param args 
    * @throws InterruptedException 
    */ 
    public static void main(String[] args) throws InterruptedException { 
     final ChangeLockObjectState test = new ChangeLockObjectState(); 

     new Thread(new Runnable() { 

      @Override 
      public void run() { 
       test.bar(); 

      } 
     }).start(); 

     Thread.sleep(6000); 

     new Thread(new Runnable() { 

      @Override 
      public void run() { 
       test.foo(); 

      } 
     }).start(); 

    } 

} 
-2

Verrà letto il nuovo valore obj.

Dalla sezione del standard Happens before:

Una scrittura un campo volatile (§8.3.1.4) accade-prima di ogni lettura successiva di quel campo.

Dalla definizione della variabile condivisa:

Tutti campi istanza, campi statici e matrice elementi sono memorizzati nella memoria mucchio. In questo capitolo, usiamo il termine variabile per riferirsi sia ai campi che agli elementi dell'array. Le variabili locali (§14.4), i parametri del metodo formale (§8.4.1) ei parametri del gestore di eccezioni (§14.20) non vengono mai condivisi tra i thread e non sono interessati dal modello di memoria.

La lettura di obj all'interno del blocco sincronizzato è separata dalla valutazione iniziale dell'espressione obj determinare accumulo nel monitor cui dell'oggetto per bloccare. La riassegnazione di obj avverrà prima della prima lettura, ma non la seconda.Poiché obj è un campo volatile, la seconda lettura deve visualizzare il valore aggiornato di obj.

+0

come è collegato a questa domanda? – Lokesh

+0

Oops, hai ragione. Risposta alla domanda sbagliata :-( –

+0

Modificato per rispondere alla domanda corretta –

0

Viene visualizzato il nuovo valore. E funziona anche senza fare objvolatile. Questo perché la sincronizzazione è ancora attiva sul vecchio oggetto e fornisce visibilità al nuovo valore una volta che il thread in attesa (t_foo) entra. Ecco il test:

public class Main3 { 
    private MyObject obj = new MyObject(1); 

    void foo() 
    { 
     synchronized(obj) 
     { 
      System.out.println(obj.number); 
      obj.doWork(); 
     } 
    } 

    void bar() 
    { 
     synchronized(obj) 
     { 
      System.out.println(obj.number); 

      obj.doWork(); 

      //force the foo thread to wait at the synchronization point 
      for(int i = 0; i < 1000000000l; i++); 

      obj = new MyObject(2); // <<<< notice this line (call it line-x) 
     } 
    } 

    public static void main(String[] args) throws InterruptedException { 
     final Main3 m3 = new Main3(); 

     Thread t1 = new Thread(new Runnable() { 
      @Override 
      public void run() { 
       m3.bar(); 
      } 
     }); 

     Thread t2 = new Thread(new Runnable() { 
      @Override 
      public void run() { 
       m3.foo(); 
      } 
     }); 

     t1.start(); 
     t2.start(); 
    } 
} 

class MyObject { 
    int number; 

    public MyObject(int number) { 
     this.number = number; 
    } 

    public void doWork() { 
    } 
} 
2

Nella situazione esatta che hai descritto, sì, la lettura di obj all'interno del blocco sincronizzato foo vedrà il nuovo valore impostato dal blocco sincronizzato della barra precedente.

La parte divertente è che non sempre accade in quella situazione esatta. Il programma non è thread-safe, ad esempio, se immediatamente dopo le uscite bar(), gli stessi thread invocano un altro bar(), mentre il thread foo si sta bloccando sul vecchio oggetto. Il blocco barra si blocca sul nuovo oggetto, quindi i due thread vengono eseguiti contemporaneamente, entrambi eseguendo obj.doWork() sullo stesso nuovo oggetto.

Possiamo probabilmente parzialmente fissarlo

// suppose this line happens-before foo()/bar() calls 
MyObject obj = new MyObject(); 

void foo() 
    while(true) 
     MyObject tmp1 = obj; 
     synchronized(tmp1) 
      MyObject tmp2 = obj; 
      if(tmp2==tmp1) 
       tmp2.doWork(); 
       return; 
      // else retry 

questo almeno garanzie non invocazioni correnti di obj.doWork() sulla stessa obj, poiché obj.doWork() può verificarsi solo in un blocco sincronizzato che blocca la stessa obj

+0

Come mai 'pippo' sta ancora bloccando l'oggetto' vecchio'? Dopo l'uscita di 'bar',' obj' avrà avuto un nuovo valore ovunque, sì? –

+0

@ Uno No, foo manterrà ancora un blocco per il vecchio oggetto.Vedi http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.19, o guarda la risposta alla mia domanda 2 edits fa (dove ho risposto * quella * domanda –

+0

Ok, tiene il blocco del vecchio oggetto, che ho capito.Ma sembra un riferimento * dentro * il blocco di sincronizzazione si riferirà al nuovo valore ob 'obj' ...? –