2011-09-26 5 views
15

Cosa significa metodo AtomicXXX.lazySet (valore) in termini di fronti before-before, utilizzato nella maggior parte dei motivi JMM? Il javadocs è puro su di esso, e Sun bug 6275329 stati:AtomicXXX.lazySet (...) in termini di accadimenti prima dei bordi

La semantica è che la scrittura è garantito di non essere riordinata con qualsiasi scrittura precedente, ma può essere riordinata con operazioni successive (o equivalentemente, potrebbe non essere visibile ad altri thread) finché non si verifica qualche altra azione volatile di scrittura o sincronizzazione).

Ma questo non è un ragionamento sui bordi HB, quindi mi confonde. Significa ciò che la semantica lazySet() non può essere espressa in termini di bordi HB?

UPDATE: Proverò a concretizzare la mia domanda. Posso usare campo volatili ordinaria nel seguente scenario:

//thread 1: producer 
...fill some data structure 
myVolatileFlag = 1; 

//thread 2: consumer 
while(myVolatileFlag!=1){ 
    //spin-wait 
} 
...use data structure... 

In questo uso scenario di "struttura dati" in consumatore è corretto, dal momento che la bandiera volatili scrittura-lettura rendere bordo HB, dando garanzia che cosa tutte le scritture di "struttura di dati "dal produttore sarà completato, e visibile dal consumatore. Ma cosa succede se userò AtomicInteger.lazySet/get invece di scrivere/leggere volatile in questo scenario?

//thread 1: producer 
...fill some data structure 
myAtomicFlag.lazySet(1); 

//thread 2: consumer 
while(myAtomicFlag.get()!=1){ 
    //spin-wait 
} 
...use data structure... 

sarà ancora corretto? Posso ancora realmente vedere la visibilità dei valori della "struttura dei dati" nel thread dei consumatori?

non è "dall'aria" questione - ho visto tale metodo in LMAX codice Disruptor esattamente questo scenario, e non capisco come dimostrarlo è corretta ...

+0

se il metodo lazySet funge da barriera di store-store, è corretto utilizzarlo. Una volta che thread2 vede il flag, sarà ok leggere la struttura. Il lazySet assicura che la scrittura sia visibile ad un certo punto e tutte le architetture consentono la visibilità automatica, ad esempio se qualcosa è stato scritto sarà visibile ed eventualmente coerente. – bestsss

risposta

10

Le operazioni lazySet non creano bordi prima e quindi non sono garantiti per essere immediatamente visibili. Questa è un'ottimizzazione di basso livello che ha solo pochi casi d'uso, che sono per lo più in strutture di dati concorrenti.

L'esempio di garbage collection di annullamento dei puntatori di elenchi collegati non ha effetti secondari visibili all'utente. Il nullamento è preferito in modo che se i nodi nell'elenco si trovano in generazioni diverse, non impone una raccolta più costosa da eseguire per scartare la catena di collegamento.L'uso di lazySet mantiene la semantica hygenic senza incorrere in costi di scrittura volatili.

Un altro esempio è l'utilizzo di campi volatili protetti da un blocco, ad esempio in ConcurrentHashMap. I campi sono volatili per consentire letture senza blocco, ma le scritture devono essere eseguite sotto un lucchetto per garantire una coerenza rigorosa. Dato che il blocco garantisce il vantaggio prima del rilascio, un'ottimizzazione consiste nell'utilizzare lazySet durante la scrittura nei campi e lo svuotamento di tutti gli aggiornamenti durante lo sblocco. Ciò aiuta a mantenere breve la sezione critica, evitando bancarelle inutili e traffico del bus.

Se si scrive una struttura di dati simultanea, allora lazySet è un buon trucco da tenere presente. È un'ottimizzazione di basso livello, quindi vale la pena considerarla solo quando si esegue l'ottimizzazione delle prestazioni.

+0

Grazie per i chiarimenti. Potresti anche dare un'occhiata all'aggiornamento e commentarlo? – BegemoT

+0

Come set, non a cas, è sicuro solo se non sovrascrive i dati importanti quando resi visibili. Più produttori competerebbero per uno slot, quindi non è sicuro. Sarebbe sicuro se un singolo utente prosciugasse il buffer, ritardando la visibilità di uno slot libero. Ci stavo giocando qualche mese fa, vedi il mio [ring buffer] (http://code.google.com/p/concurrentlinkedhashmap/source/browse/trunk/src/main/java/com/googlecode/concurrentlinkedhashmap/RingBuffer .java? spec = svn754 & r = 749) codice. –

+0

È un singolo produttore/caso singolo consumatore. Ma ancora non capisco perché sia ​​sicuro - come posso provare che cosa 1) scrive prima che sequence.lazySet (seq) sia tutto finito e _visible_ to thread 2 dopo sequence.get() == seq? e che cosa 2) lazySet (seq) è in realtà scritto in un tempo finito: i documenti dicono "alla fine" - che può significare "in secondi", "in ora" e persino "mai". Se guardi su lazySet come non volatile scrivi sembra possa essere ritardato per sempre dal compilatore o dal processore ... – BegemoT

3

Sulla base di Javadoc di Unsafe (la putOrderedInt è utilizzato nel AtomicInteger.lazySet)

/** 
* Version of {@link #putObjectVolatile(Object, long, Object)} 
* that does not guarantee immediate visibility of the store to 
* other threads. This method is generally only useful if the 
* underlying field is a Java volatile (or if an array cell, one 
* that is otherwise only accessed using volatile accesses). 
*/ 
public native void putOrderedObject(Object o, long offset, Object x); 

/** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)} */ 
public native void putOrderedInt(Object o, long offset, int x); 

i campi backing delle classi AtomicXXX sono volatili. Il lazySet sembra scrivere su questi campi come se non fossero volatili, il che rimuoverà gli spigoli che accadono prima che ti aspetti. Come indicato nel tuo link, sarebbe utile che i valori di annullamento siano idonei per GC senza dover sostenere la scrittura volatile.

Modifica:

Questo è per rispondere al vostro aggiornamento.

Se dai un'occhiata al preventivo che hai fornito dal link, perdi tutte le garanzie di memoria che hai avuto con la scrittura volatile.

Il lazySet non verrà ordinato sopra dove è stato scritto, ma senza alcuna altra sincronizzazione effettiva si perde la garanzia che il consumatore vedrà eventuali modifiche che sono state scritte prima. È perfettamente legale ritardare la scrittura di myAtomicFlag e lì per qualsiasi scrittura prima che si verifichi qualche altra forma di sincronizzazione.

+0

il lazySet garantisce nessun riordino della scrittura, ma non lo fa per leggerne uno, cioè le letture consecutive possono essere eseguite prima che il valore venga propagato. per x86 lazySet è solo un'istruzione mov. Nell'esempio sopra, lazySet va bene, imo ... ed è una grande vittoria. – bestsss