Riferimento volatile Java rispetto a AtomicReference


135

C'è qualche differenza tra un volatileriferimento all'oggetto e AtomicReferencenel caso dovessi usare get()e set()-methods da AtomicReference?

Risposte:


114

La risposta breve è: No.

Dalla java.util.concurrent.atomicdocumentazione del pacchetto. Per citare:

Gli effetti di memoria per gli accessi e gli aggiornamenti di atomica generalmente seguono le regole per i volatili:

  • getha gli effetti di memoria della lettura di una volatilevariabile.
  • setha gli effetti di memoria della scrittura (assegnazione) di una volatilevariabile.

A proposito, quella documentazione è molto buona e tutto è spiegato.


AtomicReference::lazySetè una nuova operazione (Java 6+) introdotta che ha una semantica irraggiungibile attraverso le volatilevariabili. Vedi questo post per maggiori informazioni.


11
E la risposta più lunga sarebbe?
Julien Grenier,

Concordato. Abbiamo almeno bisogno di un link.
Julien Chastang,


42

No non c'è.

La potenza aggiuntiva fornita da AtomicReference è il metodo compareAndSet () e gli amici. Se non sono necessari tali metodi, un riferimento volatile fornisce la stessa semantica di AtomicReference.set () e .get ().


14

Esistono diverse differenze e compromessi:

  1. L'uso di AtomicReferenceget / set ha la stessa semantica JMM di un campo volatile (come afferma javadoc), ma AtomicReferenceè un wrapper attorno a un riferimento, quindi qualsiasi accesso al campo comporta un ulteriore inseguimento del puntatore .

  2. L' impronta di memoria viene moltiplicata (presupponendo un ambiente OOP compresso, che è vero per la maggior parte delle macchine virtuali):

    • riferimento volatile = 4b
    • AtomicReference = 4b + 16b (intestazione oggetto 12b + campo riferimento 4b)
  3. AtomicReferenceoffre un'API più ricca di un riferimento volatile. È possibile recuperare l'API per il riferimento volatile utilizzando un AtomicFieldUpdatero con Java 9 a VarHandle. Puoi anche raggiungere dritto sun.misc.Unsafese ti piace correre con le forbici. AtomicReferencestesso viene implementato usando Unsafe.

Quindi, quando è bello scegliere l'uno rispetto all'altro:

  • Hai solo bisogno di get / set? Stick con un campo volatile, la soluzione più semplice e il minimo overhead.
  • Hai bisogno di funzionalità extra? Se si tratta di una parte sensibile delle prestazioni (sovraccarico di velocità / memoria) del tuo codice, fai una scelta tra AtomicReference/ AtomicFieldUpdater/ Unsafedove tendi a pagare in leggibilità e rischi per il tuo guadagno in termini di prestazioni. Se questa non è un'area sensibile, cerca semplicemente AtomicReference. Gli autori di librerie utilizzano in genere una combinazione di questi metodi in base a JDK mirati, restrizioni API previste, vincoli di memoria e così via.

7

Il codice sorgente JDK è uno dei modi migliori per rispondere a confusioni come questa. Se si osserva il codice in AtomicReference, utilizza una variabile volatie per l'archiviazione degli oggetti.

private volatile V value;

Quindi, ovviamente, se hai intenzione di usare solo get () e set () su AtomicReference è come usare una variabile volatile. Ma come altri lettori hanno commentato, AtomicReference fornisce ulteriore semantica CAS. Quindi, prima decidi se vuoi la semantica CAS o meno, e se lo fai solo allora usa AtomicReference.


13
"Il codice sorgente JDK è uno dei modi migliori per rispondere a confusioni come questa" => Non necessariamente sono d'accordo - il javadoc (che è il contratto della classe) è il modo migliore. Quello che trovi nel codice risponde alla domanda per un'implementazione specifica ma il codice può cambiare.
Assylias,

4
Ad esempio questa variabile in hashmap era volatile in JDK 6 ma non è più volatile in Java 7. Hai basato il tuo codice sul fatto che la variabile era volatile, si sarebbe rotta quando si usava il tuo JDK ... Certamente l'esempio è diverso ma ottieni il punto.
Assylias,

Quel CAS è un'abbreviazione standard?
abbas

1
Confronta e Scambia =)
infinito

4

AtomicReferencefornisce funzionalità aggiuntive che non fornisce una semplice variabile volatile. Dopo aver letto l'API Javadoc, lo saprai, ma fornisce anche un blocco che può essere utile per alcune operazioni.

Tuttavia, a meno che tu non abbia bisogno di questa funzionalità aggiuntiva, ti suggerisco di utilizzare un volatilecampo semplice .


Quindi la differenza, quindi, sta nelle loro prestazioni. Se non ci fosse differenza, non suggeriresti mai di usarne uno rispetto all'altro.
BT

Le prestazioni sono più o meno le stesse. Un AtomicRefrence aggiunge complessità e utilizzo della memoria.
Peter Lawrey,

@BT Un volatilecampo può essere usato come qualsiasi altro campo mentre l'accesso al valore AtomicReferencerichiede un passaggio gete setmetodi.
David Harkness,

0

A volte anche se usi solo get e set, AtomicReference potrebbe essere una buona scelta:

Esempio con volatile:

private volatile Status status;
...
public setNewStatus(Status newStatus){
  status = newStatus;
}

public void doSomethingConditionally() {
  if(status.isOk()){
    System.out.println("Status is ok: " + status); // here status might not be OK anymore because in the meantime some called setNewStatus(). setNewStatus should be synchronized
  }
}

L'implementazione con AtomicReference ti darebbe gratuitamente una sincronizzazione copia su scrittura.

private AtomicReference<Status> statusWrapper;
...

public void doSomethingConditionally() {
  Status status = statusWrapper.get();
  if(status.isOk()){
    System.out.println("Status is ok: " + status); // here even if in the meantime some called setNewStatus() we're still referring to the old one
  }
}

Si potrebbe dire che potresti ancora avere una copia corretta se sostituissi:

Status status = statusWrapper.get();

con:

Status statusCopy = status;

Tuttavia è più probabile che il secondo venga rimosso da qualcuno in futuro durante la "pulizia del codice".

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.