Réponses:
La réponse courte est: Non.
À partir de la java.util.concurrent.atomic
documentation du package. Citer:
Les effets mémoire pour les accès et les mises à jour des atomiques suivent généralement les règles pour les volatiles:
get
a les effets mémoire de la lecture d'unevolatile
variable.set
a les effets de mémoire d'écrire (d'affecter) unevolatile
variable.
Au fait, cette documentation est très bonne et tout est expliqué.
AtomicReference::lazySet
est une opération plus récente (Java 6+) introduite dont la sémantique est irréalisable via des volatile
variables. Voir cet article pour plus d'informations.
Il existe plusieurs différences et compromis:
L'utilisation d'un AtomicReference
get / set a la même sémantique JMM qu'un champ volatile (comme l'indique javadoc), mais AtomicReference
est un wrapper autour d'une référence, donc tout accès au champ implique une poursuite de pointeur supplémentaire .
L' empreinte mémoire est multipliée (en supposant un environnement de POO compressé, ce qui est vrai pour la plupart des machines virtuelles):
AtomicReference
= 4b + 16b (en-tête d'objet 12b + champ de référence 4b)AtomicReference
offre une API plus riche qu'une référence volatile. Vous pouvez récupérer l'API pour la référence volatile en utilisant un AtomicFieldUpdater
, ou avec Java 9 a VarHandle
. Vous pouvez également atteindre directement sun.misc.Unsafe
si vous aimez courir avec des ciseaux. AtomicReference
lui-même est implémenté en utilisant Unsafe
.
Alors, quand est-il bon de choisir l'un plutôt que l'autre:
AtomicReference
/ AtomicFieldUpdater
/ Unsafe
où vous avez tendance à payer en lisibilité et à prendre des risques pour votre gain de performances. Si ce n'est pas une zone sensible, allez-y AtomicReference
. Les rédacteurs de bibliothèque utilisent généralement une combinaison de ces méthodes en fonction des JDK ciblés, des restrictions d'API attendues, des contraintes de mémoire, etc.Le code source JDK est l'un des meilleurs moyens de répondre à de telles confusions. Si vous regardez le code dans AtomicReference, il utilise une variable volatie pour le stockage d'objets.
private volatile V value;
Donc, évidemment, si vous allez simplement utiliser get () et set () sur AtomicReference, c'est comme utiliser une variable volatile. Mais comme l'ont commenté d'autres lecteurs, AtomicReference fournit une sémantique CAS supplémentaire. Alors, décidez d'abord si vous voulez ou non la sémantique CAS, et si vous ne le faites que, utilisez AtomicReference.
AtomicReference
fournit des fonctionnalités supplémentaires qu'une variable volatile simple ne fournit pas. Comme vous avez lu l'API Javadoc, vous le saurez, mais il fournit également un verrou qui peut être utile pour certaines opérations.
Cependant, à moins que vous n'ayez besoin de cette fonctionnalité supplémentaire, je vous suggère d'utiliser un volatile
champ simple .
volatile
champ peut être utilisé comme n'importe quel champ normal alors que l'accès à la valeur dans un AtomicReference
nécessite un passage par get
et des set
méthodes.
Parfois, même si vous n'utilisez que des get et des ensembles, AtomicReference peut être un bon choix:
Exemple avec 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'implémentation avec AtomicReference vous donnerait une synchronisation copie sur écriture gratuitement.
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
}
}
On pourrait dire que vous pourriez toujours avoir une copie correcte si vous remplaçiez:
Status status = statusWrapper.get();
avec:
Status statusCopy = status;
Cependant, le second est plus susceptible d'être supprimé par quelqu'un accidentellement à l'avenir pendant le "nettoyage du code".