Référence volatile Java vs AtomicReference

135

Y a-t-il une différence entre une volatileréférence d'objet et AtomicReferenceau cas où j'utiliserais simplement get()et set()-méthodes de AtomicReference?

auramo
la source

Réponses:

114

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'une volatilevariable.
  • seta les effets de mémoire d'écrire (d'affecter) une volatilevariable.

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.

pgras
la source
11
Et la réponse la plus longue serait?
Julien Grenier
D'accord. Nous avons au moins besoin d'un lien.
Julien Chastang
2
Le lien vers une réponse plus longue: java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/atomic/…
Alex Siman
42

Non, il n'y en a pas.

La puissance supplémentaire fournie par AtomicReference est la méthode compareAndSet () et ses amis. Si vous n'avez pas besoin de ces méthodes, une référence volatile fournit la même sémantique que AtomicReference.set () et .get ().

Avi
la source
14

Il existe plusieurs différences et compromis:

  1. 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 .

  2. L' empreinte mémoire est multipliée (en supposant un environnement de POO compressé, ce qui est vrai pour la plupart des machines virtuelles):

    • réf volatile = 4b
    • AtomicReference = 4b + 16b (en-tête d'objet 12b + champ de référence 4b)
  3. 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:

  • Besoin seulement de get / set? Tenez-vous en à un champ volatil, à la solution la plus simple et aux frais généraux les plus bas.
  • Besoin de fonctionnalités supplémentaires? S'il s'agit d'une partie de votre code sensible aux performances (vitesse / surcharge de mémoire), faites un choix entre 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.
Nitsan Wakart
la source
7

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.

interminable
la source
13
"Le code source JDK est l'un des meilleurs moyens de répondre à des confusions comme celle-ci" => Je ne suis pas forcément d'accord - le javadoc (qui est le contrat de la classe) est le meilleur moyen. Ce que vous trouvez dans le code répond à la question d'une implémentation spécifique, mais le code peut changer.
assylias
4
Par exemple cette variable dans hashmap était volatile dans JDK 6 mais n'est plus volatile dans Java 7. Avez-vous basé votre code sur le fait que la variable était volatile, elle se serait cassée lors de la mise à niveau de votre JDK ... Certes, l'exemple est différent mais vous obtenez le point.
assylias
Est-ce que CAS est une abréviation standard?
abbas
1
Compare and Swap =)
interminable
4

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 .

Peter Lawrey
la source
La différence réside donc dans leurs performances. S'il n'y avait pas de différence, vous ne suggéreriez jamais d'utiliser l'un sur l'autre.
BT
La performance est sensiblement la même. Un AtomicRefrence ajoute de la complexité et de l'utilisation de la mémoire.
Peter Lawrey
@BT Un 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.
David Harkness
0

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".

nme
la source