AtomicInteger lazySet vs. set


116

Qual è la differenza tra i metodi lazySete setdi AtomicInteger? La documentazione non ha molto da dire su lazySet:

Alla fine si imposta sul valore dato.

Sembra che il valore memorizzato non verrà impostato immediatamente sul valore desiderato, ma verrà invece programmato per essere impostato in futuro. Ma qual è l'uso pratico di questo metodo? Qualche esempio?

Risposte:


114

Citato direttamente da "JDK-6275329: aggiungi metodi lazySet alle classi atomiche" :

Come probabilmente l'ultimo piccolo seguito di JSR166 per Mustang, abbiamo aggiunto un metodo "lazySet" alle classi Atomic (AtomicInteger, AtomicReference, ecc.). Questo è un metodo di nicchia che a volte è utile quando si ottimizza il codice utilizzando strutture di dati non bloccanti. La semantica è che è garantito che la scrittura non verrà riordinata con alcuna scrittura precedente, ma potrebbe essere riordinata con operazioni successive (o, in modo equivalente, potrebbe non essere visibile ad altri thread) fino a quando non si verifica qualche altra azione volatile di scrittura o sincronizzazione).

Il caso d'uso principale è l'annullamento dei campi dei nodi in strutture di dati non bloccanti al solo scopo di evitare la conservazione dei rifiuti a lungo termine; si applica quando è innocuo se altri thread vedono valori non nulli per un po ', ma vorresti assicurarti che le strutture siano eventualmente GCable. In questi casi, è possibile ottenere prestazioni migliori evitando i costi della scrittura volatile nulla. Ci sono alcuni altri casi d'uso in questo senso anche per atomici non basati su riferimenti, quindi il metodo è supportato in tutte le classi AtomicX.

Per le persone a cui piace pensare a queste operazioni in termini di barriere a livello di macchina su multiprocessori comuni, lazySet fornisce una precedente barriera negozio-negozio (che è una barriera non operativa o molto economica sulle piattaforme attuali), ma nessuna barriera di carico del negozio (che di solito è la parte costosa di una scrittura volatile).


14
Qualcuno potrebbe sminuirlo per il resto di noi? :(
Gaurav

14
Lazy è la versione non volatile (ad esempio, non è garantito che il cambio di stato sia visibile a tutti i thread che hanno l' Atomic*ambito).
sbadiglio

63
quello che non capisco è perché javadoc è così povero al riguardo.
Felipe

8
Sono sicuro che alla fine riusciranno a cambiarlo. Boom boom.
MMJZ

3
per coloro che vogliono saperne di più sulla barriera negozio / carico e perché la barriera negozio-negozio è più economica della barriera carico-negozio. Ecco un articolo di facile comprensione a riguardo. Mechanical-sympathy.blogspot.com/2011/07/…
Kin Cheung

15

lazySet può essere utilizzato per la comunicazione tra thread rmw, perché xchg è atomico, per quanto riguarda la visibilità, quando il processo del thread del writer modifica una posizione della riga della cache, il processore del thread del lettore lo vedrà alla lettura successiva, perché il protocollo di coerenza della cache della CPU Intel lo garantirà LazySet funziona, ma la riga della cache verrà aggiornata alla prossima lettura, ancora una volta, la CPU deve essere abbastanza moderna.

http://sc.tamu.edu/systems/eos/nehalem.pdf Per Nehalem, che è una piattaforma multiprocessore, i processori hanno la capacità di "spiare" (intercettare) il bus degli indirizzi per gli accessi di altri processori alla memoria di sistema e nelle loro cache interne. Usano questa capacità di snooping per mantenere le loro cache interne coerenti sia con la memoria di sistema che con le cache in altri processori interconnessi. Se attraverso lo snooping un processore rileva che un altro processore intende scrivere in una posizione di memoria che ha attualmente memorizzato nella cache in stato Condiviso, il processore snooping invaliderà il blocco della cache costringendolo a eseguire un riempimento della riga della cache la prossima volta che accede alla stessa posizione di memoria .

oracle hotspot jdk per architettura cpu x86->

lazySet == unsafe.putOrderedLong == xchg rw (istruzione asm che funge da barriera morbida che costa 20 cicli su CPU Nehelem Intel)

su x86 (x86_64) tale barriera è molto più economica in termini di prestazioni rispetto a volatile o AtomicLong getAndAdd,

In un produttore, uno scenario di coda consumatore, la barriera morbida xchg può forzare la riga di codici prima del lazySet (sequenza + 1) affinché il thread del produttore avvenga PRIMA di qualsiasi codice del thread del consumatore che consumerà (lavorerà) sui nuovi dati, ovviamente il thread del consumatore dovrà controllare atomicamente che la sequenza del produttore sia stata incrementata esattamente di uno utilizzando un compareAndSet (sequenza, sequenza + 1).

Ho tracciato dopo il codice sorgente di Hotspot per trovare la mappatura esatta del codice lazySet su cpp: http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/prims/unsafe. cpp Unsafe_setOrderedLong -> SET_FIELD_VOLATILE definition -> OrderAccess: release_store_fence. Per x86_64, OrderAccess: release_store_fence viene definito utilizzando l'istruzione xchg.

Puoi vedere come è esattamente definito in jdk7 (doug lea sta lavorando su alcune nuove cose per JDK 8): http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/4fc084dac61e/src/os_cpu/ linux_x86 / vm / orderAccess_linux_x86.inline.hpp

puoi anche usare gli hdis per disassemblare l'assembly del codice lazySet in azione.

C'è un'altra domanda correlata: abbiamo bisogno di mfence quando si usa xchg


5
È difficile capire cosa stai ottenendo qui. Puoi per favore chiarire il tuo punto?
Paul Bellora

3
"lazySet == unsafe.putOrderedLong == xchg rw (istruzione asm che funge da barriera morbida che costa 20 cicli su cpu nehelem intel) su x86 (x86_64) tale barriera è molto più economica in termini di prestazioni rispetto a volatile o AtomicLong getAndAdd" -> Questo non è vero per quanto ne so. lazySet / putOrdered è un MOV per un indirizzo, motivo per cui il ricettario JMM lo descrive come un divieto su x86.
Nitsan Wakart

11

Una discussione più ampia sulle origini e l'utilità di lazySet e del sottostante putOrdered può essere trovata qui: http://psy-lob-saw.blogspot.co.uk/2012/12/atomiclazyset-is-performance-win-for.html

Per riassumere: lazySet è una scrittura volatile debole, nel senso che agisce come un negozio-negozio e non un recinto di carico del negozio. Questo si riduce al fatto che lazySet viene compilato JIT su un'istruzione MOV che non può essere riordinata dal compilatore piuttosto che l'istruzione significativamente più costosa utilizzata per un set volatile.

Quando si legge il valore si finisce sempre per eseguire una lettura volatile (con un Atomic * .get () in ogni caso).

lazySet offre a un singolo writer un meccanismo di scrittura volatile coerente, ovvero è perfettamente legittimo che un singolo writer utilizzi lazySet per incrementare un contatore, più thread che incrementano lo stesso contatore dovranno risolvere le scritture concorrenti utilizzando CAS, che è esattamente ciò che accade sotto le copertine di Atomic * per incAndGet.


esattamente, perché non possiamo dire che questa è una semplice StoreStorebarriera, ma non una StoreLoad?
Eugene

8

Dal riepilogo del pacchetto atomico simultaneo

lazySet ha gli effetti sulla memoria della scrittura (assegnazione) di una variabile volatile tranne per il fatto che consente il riordino con azioni di memoria successive (ma non precedenti) che non impongono vincoli di riordino con le normali scritture non volatili. Tra gli altri contesti di utilizzo, lazySet può essere applicato quando si annulla, per motivi di garbage collection, un riferimento a cui non si accede mai più.

Se sei curioso di lazySet allora devi anche a te stesso altre spiegazioni

Gli effetti di memoria per gli accessi e gli aggiornamenti di atomics generalmente seguono le regole per i volatili, come indicato nella sezione 17.4 di The Java ™ Language Specification.

get ha gli effetti di memoria della lettura di una variabile volatile.

set ha gli effetti di memoria di scrivere (assegnare) una variabile volatile.

lazySet ha gli effetti sulla memoria della scrittura (assegnazione) di una variabile volatile tranne per il fatto che consente il riordino con azioni di memoria successive (ma non precedenti) che non impongono vincoli di riordino con le normali scritture non volatili. Tra gli altri contesti di utilizzo, lazySet può essere applicato quando si annulla, per motivi di garbage collection, un riferimento a cui non si accede mai più.

weakCompareAndSet legge in modo atomico e scrive in modo condizionale una variabile ma non crea alcun ordine accade prima, quindi non fornisce garanzie rispetto alle letture e scritture precedenti o successive di variabili diverse dalla destinazione di weakCompareAndSet.

compareAndSet e tutte le altre operazioni di lettura e aggiornamento come getAndIncrement hanno gli effetti sulla memoria sia della lettura che della scrittura di variabili volatili.


4

Ecco la mia comprensione, correggimi se sbaglio: Puoi pensare lazySet()come "semi" volatile: è fondamentalmente una variabile non volatile in termini di lettura da parte di altri thread, cioè il valore impostato da lazySet potrebbe non essere visibile ad altri filettature. Ma diventa volatile quando si verifica un'altra operazione di scrittura (potrebbe provenire da altri thread). L'unico impatto di lazySet che posso immaginare è compareAndSet. Quindi, se lo usi lazySet(), get()da altri thread potresti comunque ottenere il vecchio valore, ma compareAndSet()avrà sempre il nuovo valore poiché è un'operazione di scrittura.


1
non intendi compareAndSet?
Dave Moten

2

Ri: tentativo di smorzarlo -

Puoi pensare a questo come a un modo per trattare un campo volatile come se non fosse volatile per una particolare operazione di archivio (ad esempio: ref = null;).

Non è perfettamente accurato, ma dovrebbe essere sufficiente che tu possa prendere una decisione tra "OK, davvero non mi interessa" e "Hmm, fammi pensare un po '".

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.