Réponses:
La réponse courte est: Non.
À partir de la java.util.concurrent.atomicdocumentation 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:
geta les effets mémoire de la lecture d'unevolatilevariable.seta les effets de mémoire d'écrire (d'affecter) unevolatilevariable.
Au fait, cette documentation est très bonne et tout est expliqué.
AtomicReference::lazySetest une opération plus récente (Java 6+) introduite dont la sémantique est irréalisable via des volatilevariables. Voir cet article pour plus d'informations.
Il existe plusieurs différences et compromis:
L'utilisation d'un AtomicReferenceget / set a la même sémantique JMM qu'un champ volatile (comme l'indique javadoc), mais AtomicReferenceest 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)AtomicReferenceoffre 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.Unsafesi vous aimez courir avec des ciseaux. AtomicReferencelui-même est implémenté en utilisant Unsafe.
Alors, quand est-il bon de choisir l'un plutôt que l'autre:
AtomicReference/ AtomicFieldUpdater/ Unsafeoù 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.
AtomicReferencefournit 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 volatilechamp simple .
volatilechamp peut être utilisé comme n'importe quel champ normal alors que l'accès à la valeur dans un AtomicReferencenécessite un passage par getet des setmé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".