2010-11-15 24 views
7

Questo è il mio Java 1.6 classe:Cosa significa "sincronizzazione incoerente"?

public class Foo { 
    private ArrayList<String> names; 
    public void scan() { 
    if (names == null) { 
     synchronized (this) { 
     this.names = new ArrayList<String>(); 
     // fill the array with data 
     } 
    } 
    } 
} 

Findbugs dice:

Inconsistent synchronization of com.XXX.Foo.names; locked 40% of time 

Che cosa significa e che cosa sto facendo male? Sto cercando di evitare problemi quando due o più client chiamano allo stesso tempo Foo.scan().

risposta

15

È normale che si sta eseguendo la sincronizzazione solo quando si imposta la variabile names e non quando la si legge. Così tra la lettura e la scrittura potrebbe essere eseguito un altro thread e tu dovresti creare due liste di array e riempirle di dati, il primo creato otterrebbe GC'ed.

È necessario inserire il blocco sincronizzato attorno alla lettura e alla scrittura o aggiungere il modificatore sincronizzato al metodo.

public class Foo { 
    private ArrayList<String> names; 
    public void scan() { 
     synchronized (this) 
     if (names == null) { 
      this.names = new ArrayList<String>(); 
      // fill the array with data 
     } 
     } 
    } 
    } 
+1

Se si prevede di utilizzare questo idioma, la variabile dei nomi deve essere contrassegnata come volatile. –

+0

Dovresti anche controllare se 'names' è nullo prima di inserire il blocco sranoize. Questo evita il blocco quando non è necessario. – Jeremy

+0

@Phil M perché i nomi dovrebbero essere contrassegnati come volatili? Ho pensato che synchonized fornisse la stessa visibilità di volatile. Quindi non aggiungerei nulla finché l'accesso ai nomi utilizza lo stesso blocco. – brain

6

La prima volta che si fa riferimento a names all'interno scan è al di fuori della synchronized blocco.
esempio se scan viene chiamato due volte da due fili diversi e names è nullo, esso può andare così

  1. if (names == null) dal primo filo viene elaborata (a true).
  2. if (names == null) dal secondo thread viene elaborato (a true).
  3. Il primo thread immette nel blocco synchronized, assegna names e lascia il blocco synchronized.
  4. Il secondo thread immette nel blocco synchronized, assegna names e lascia il blocco synchronized.

Ora, names viene inizializzato due volte. E questo è solo uno dei possibili scenari in cui ottieni risultati inaspettati.

+0

'bloccato il 40% del tempo 'cosa significa in avviso –

+0

@ org.life.java Nessuna idea, forse questo non è l'intero codice. –