2016-05-11 34 views
5

In Java, quando è possibile "allontanarsi" se non si utilizzano sincronizzati su variabili che sono in lettura/scrittura per più thread simultanei?Quando è sicuro non sincronizzare le variabili di lettura/scrittura?

Ho letto di un paio di sorprendenti bug di concorrenza: double-checked-locking e hash-maps e ho sempre utilizzato la sincronizzazione per impostazione predefinita in casi di lettura/scrittura condivisi, tuttavia, ho iniziato a chiedermi quando è OK non farlo.

Per esempio, che tipo di regole generali posso usare per decidere quando è realtà sicuro di omettere synchronized dai metodi come questo:

T getFoo() { 
    if (this.foo == null) { 
    this.foo = createFoo() // createFoo is always thread safe. 
    } 
    return this.foo; 
} 

Dove:

  • T potrebbe essere primitivi o oggetti arbitrari
  • createFoo è sempre thread-safe e può essere chiamato più volte, ma è altrimenti unsp ecified.
  • getFoo() può essere "alla fine coerente".

Va bene se T sono primitive come int? Che mi dici di Integer? Che dire degli oggetti semplici, come String? Etc.

+3

Non è mai giusto non farlo, a meno che non si può in qualche modo semanticamente garanzia che non sarà la lettura e la scrittura allo stesso tempo (o se non si importa se alcuni dei vostri lavoratori stanno leggendo il torto cosa dopo che è stato aggiornato) –

+1

Solo se si sa in qualsiasi momento, ci sarà solo un thread per accedervi. – sauumum

+3

* Non c'è ** nessuna garanzia ** che il valore sarà visto da ** qualsiasi ** thread dopo che è stato aggiornato * senza la sincronizzazione appropriata ('synchronized' è un metodo comune, ma non l'unico). La sicurezza del filo è più di semplici istruzioni/programmi interlacciati. – user2864740

risposta

2

Quando è opportuno non sincronizzare le variabili di lettura/scrittura?

La risposta tongue-in-cheek è Solo quando si comprende appieno le implicazioni per l'hardware sottostante, la JVM, e la vostra applicazione. Se possibile, consiglierei comunque questo approccio, che inizia con molte letture ed esperimenti.

In pratica, dovresti essere in grado di utilizzare alcuni modelli di uso comune per ridurre al minimo la quantità di metodi o blocchi synchronized nel codice, senza comprendere tutte le complessità di detti modelli. Questo non dovrebbe aumentare troppo il rischio per la tua applicazione, perché ci sono dettagli non banali anche intorno all'uso di synchronized e se hai compreso tutti questi dettagli, avresti fatto una domanda diversa.

Ancora, il tuo chilometraggio può variare, specialmente se la forma e l'implementazione di detti modelli comunemente usati non sono benedetti dal tuo guru della concorrenza locale.

blatera Basta, mi dia qualche esempio

  1. Uso collezioni thread-safe, utilità e tipi.
    • Questo include fare affidamento su java.util.concurrent ove possibile.
    • C'è anche una quantità enorme di librerie e strumenti aggiuntivi per Java al di fuori del JDK e del suo ecosistema, ma non si qualificano facilmente per "applica ora, leggi più tardi".
  2. Utilizzando volatile
  3. Utilizzando l'inizializzazione sicura e la pubblicazione sicura
  4. Mantenere i dati locali.
    • Tipo di un non-sforzo, e sorprendentemente spesso applicabile.
+1

Ottima risposta. Gestisco diversi servizi paralleli open source di Task/Data e lascia le variabili statistiche come semplici int o long senza volatile o locking. Tuttavia, ho commenti espliciti su come "proteggere" queste variabili in caso di necessità. – edharned

1

Non è mai sicuro condividere dati tra thread senza una barriera di memoria.

La correttezza di getFoo() dipende dalla dichiarazione del campo foo.

Se getFoo() viene chiamato da più thread, ogni thread potrebbe terminare con un'istanza diversa di T. Senza una barriera di memoria, le azioni di un thread (come l'assegnazione al campo foo) potrebbero non essere mai visibili ad altri thread, mai! In altre parole, senza sincronizzazione, coerenza, "eventuale" o altro, non è garantito.

Se foo è volatile, che funge da barriera di memoria sufficiente, e poi basta solo avere il problema che più thread potrebbero ottenere una diversa T per un breve periodo di tempo nella loro corsa per creare istanze.