Risposte:
La risposta breve è: No.
Dalla java.util.concurrent.atomic
documentazione del pacchetto. Per citare:
Gli effetti di memoria per gli accessi e gli aggiornamenti di atomica generalmente seguono le regole per i volatili:
get
ha gli effetti di memoria della lettura di unavolatile
variabile.set
ha gli effetti di memoria della scrittura (assegnazione) di unavolatile
variabile.
A proposito, quella documentazione è molto buona e tutto è spiegato.
AtomicReference::lazySet
è una nuova operazione (Java 6+) introdotta che ha una semantica irraggiungibile attraverso le volatile
variabili. Vedi questo post per maggiori informazioni.
Esistono diverse differenze e compromessi:
L'uso di AtomicReference
get / 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 .
L' impronta di memoria viene moltiplicata (presupponendo un ambiente OOP compresso, che è vero per la maggior parte delle macchine virtuali):
AtomicReference
= 4b + 16b (intestazione oggetto 12b + campo riferimento 4b)AtomicReference
offre un'API più ricca di un riferimento volatile. È possibile recuperare l'API per il riferimento volatile utilizzando un AtomicFieldUpdater
o con Java 9 a VarHandle
. Puoi anche raggiungere dritto sun.misc.Unsafe
se ti piace correre con le forbici. AtomicReference
stesso viene implementato usando Unsafe
.
Quindi, quando è bello scegliere l'uno rispetto all'altro:
AtomicReference
/ AtomicFieldUpdater
/ Unsafe
dove 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.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.
AtomicReference
fornisce 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 volatile
campo semplice .
volatile
campo può essere usato come qualsiasi altro campo mentre l'accesso al valore AtomicReference
richiede un passaggio get
e set
metodi.
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".