2015-09-28 19 views
9

ho letto questo answer, alla fine del quale il seguente scritto:preferendo sincronizzati per volatili

Tutto ciò che è possibile con volatili può essere fatto con sincronizzato, ma non viceversa.

Non è chiaro. JLS 8.3.1.4 definisce campi volatili come segue:

Un campo può essere dichiarato volatile, nel qual caso la memoria Modello Java garantisce che tutte le discussioni vedono un valore coerente per la variabile (§17.4).

Quindi, i campi volatili riguardano la visibilità della memoria. Inoltre, per quanto ho ottenuto dalla risposta che ho citato, la lettura e la scrittura di campi volatili sono sincronizzati.

La sincronizzazione, a sua volta garantisce che l'unico thread abbia accesso a un blocco sincronizzato. Come ho ottenuto, non ha nulla a che fare con la visibilità della memoria. Cosa mi sono perso?

+0

L'ingresso e l'uscita da un blocco 'sincronizzato' esegue una barriera di memoria, assicurando che le modifiche siano visibili attraverso i thread. – Sneftel

risposta

6

In sincronizzazione infatti è anche legato alla visibilità memoria, come JVM aggiunge un memoria barriera in uscita del blocco sincronizzato. Ciò garantisce che i risultati delle scritture del thread nel blocco di sincronizzazione siano garantiti dalla lettura di altri thread una volta il primo thread è uscito dal blocco sincronizzato.

Nota: seguente @ commento di PaŭloEbermann, se l'altro thread passare attraverso una barriera di memoria di lettura (da ottenere in un blocco sincronizzato per esempio), la loro cache locale non sarà invalidata e quindi potrebbe leggere un vecchio valore.

L'uscita di un blocco sincronizzato è un accade-prima in questo documento: http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility

cercare questi estratti:

I risultati di una scrittura di un thread sono garantiti essere visibile a un letto da un altro thread solo se avviene l'operazione di scrittura, prima dell'operazione di lettura .

e

Uno sblocco (blocco o metodo exit sincronizzato) di un monitor accade-prima di ogni blocco successivo dello stesso monitor (blocco o metodo voce sincronizzato).E poiché la relazione before-before è transitiva, tutte le azioni di una discussione prima dello sblocco avvengono prima di tutte le azioni successive a qualsiasi thread che blocca il monitoraggio .

+0

Ottima risposta, grazie mille. Non pensavo che sotto il concetto di visibilità della memoria ci fosse un concetto più generale. –

+0

Lo capirei in modo leggermente diverso: i cambiamenti in (e in realtà anche prima) il blocco di sincronizzazione sono visibili solo ad altri thread che successivamente si sincronizzano sullo stesso oggetto (o hanno un altro modo di essere in una relazione "happen-after") , non tutte le letture di tutti gli altri thread. –

+0

@ PaŭloEbermann infatti, se gli altri thread non si sincronizzano (o sono in alcun modo provocatori di una barriera di memoria di lettura), la loro cache locale non verrà invalidata e quindi potrebbero leggere un vecchio valore. –

1

Questo è sbagliato. La sincronizzazione ha a che fare con la visibilità della memoria. Ogni thread ha una propria cache. Se hai un lucchetto, la cache è aggiornata. Se si rilascia un blocco, la cache viene trasferita nella memoria principale.

Se si legge un campo volatile, c'è anche un aggiornamento, se si scrive un campo volatile c'è un colore.

2

Sincronizzato e volatile sono diversi, ma in genere entrambi vengono utilizzati per risolvere lo stesso problema comune.

Sincronizzato è per assicurarsi che solo un thread acceda alla risorsa condivisa in un determinato punto di tempo.

Considerando che tali risorse condivise sono spesso dichiarate come volatili, è perché, se un thread ha modificato il valore della risorsa condivisa, deve essere aggiornato anche nell'altro thread. Ma senza volatilità, il runtime, semplicemente ottimizza il codice, leggendo il valore dalla cache. Quindi, che cosa è volatile, ogni volta che un thread accede volatile, non leggerà il valore dalla cache, ma in realtà lo ottiene dalla memoria effettiva e lo stesso viene utilizzato.


Passava attraverso il codice log4j e questo è quello che ho trovato.

/** 
* Config should be consistent across threads. 
*/ 
protected volatile PrivateConfig config; 
+0

Quindi perché utilizzare la parola chiave "volatile" a tutti? È possibile ottenere l'accesso multi-thread senza specificare volatile o sincronizzato –

1

Se più thread scrivono una variabile volatili comune e hanno anche bisogno di utilizzare un valore precedente di esso, può creare un race condition. Quindi a questo punto è necessario utilizzare la sincronizzazione.

... se due thread sono sia in lettura che in scrittura su una variabile condivisa, quindi l'utilizzo della parola chiave volatile non è sufficiente. In questo caso è necessario utilizzare un sincronismo per garantire che la lettura e la scrittura della variabile siano atomiche. Leggere o scrivere una variabile volatile non blocca la lettura o la scrittura dei thread. Affinché questo accada, è necessario utilizzare la parola chiave sincronizzata attorno alle sezioni critiche.

Per dettagliato tutorial su volatili, vedere 'volatile' is not always enough.

+1

Per casi semplici come un contatore thread-safe o un cas, utilizzando un [Atomic] (http://docs.oracle. com/JavaSE/7/docs/api/java/util/concorrente/Atomic/package-sintesi.html) di solito è più semplice e funziona meglio di un blocco 'sincronizzato', specialmente in alta contesa. – duckstep