Java flüchtige Referenz vs. AtomicReference

135

Gibt es einen Unterschied zwischen einer Objektreferenz volatileund AtomicReferencefür den Fall, dass ich nur get()und set()-Methoden von verwenden würde AtomicReference?

Auramo
quelle

Antworten:

114

Kurze Antwort lautet: Nein.

Aus der java.util.concurrent.atomicPaketdokumentation. Zitieren:

Die Memory-Effekte für Zugriffe und Aktualisierungen von Atomics folgen im Allgemeinen den Regeln für flüchtige Stoffe:

  • gethat die Memory-Effekte des Lesens einer volatileVariablen.
  • sethat die Memory-Effekte des Schreibens (Zuweisens) einer volatileVariablen.

Diese Dokumentation ist übrigens sehr gut und alles wird erklärt.


AtomicReference::lazySetist eine neuere (Java 6+) Operation, deren Semantik durch volatileVariablen nicht erreicht werden kann. Weitere Informationen finden Sie in diesem Beitrag .

pgras
quelle
11
Und die längere Antwort wäre?
Julien Grenier
Einverstanden. Wir brauchen zumindest einen Link.
Julien Chastang
2
Der Link zur längeren Antwort: java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/atomic/…
Alex Siman
42

Nein, da ist kein.

Die zusätzliche Leistung von AtomicReference ist die compareAndSet () -Methode und Freunde. Wenn Sie diese Methoden nicht benötigen, bietet eine flüchtige Referenz dieselbe Semantik wie AtomicReference.set () und .get ().

Avi
quelle
14

Es gibt verschiedene Unterschiede und Kompromisse:

  1. Die Verwendung eines AtomicReferenceget / set hat dieselbe JMM-Semantik wie ein flüchtiges Feld (wie im Javadoc angegeben), aber das AtomicReferenceist ein Wrapper um eine Referenz, sodass jeder Zugriff auf das Feld eine weitere Zeigerjagd erfordert .

  2. Der Speicherbedarf wird multipliziert (unter der Annahme einer komprimierten OOP-Umgebung, was für die meisten VMs gilt):

    • flüchtige ref = 4b
    • AtomicReference = 4b + 16b (12b Objektkopf + 4b Referenzfeld)
  3. AtomicReferencebietet eine umfangreichere API als eine flüchtige Referenz. Sie können die API für die flüchtige Referenz mithilfe von a AtomicFieldUpdateroder mit Java 9 a wiederherstellen VarHandle. Sie können auch geradeaus greifen, sun.misc.Unsafewenn Sie gerne mit einer Schere laufen. AtomicReferenceselbst wird mit implementiert Unsafe.

Wann ist es also gut, einen über den anderen zu wählen:

  • Benötigen Sie nur get / set? Halten Sie sich an ein flüchtiges Feld, die einfachste Lösung und den geringsten Overhead.
  • Benötigen Sie die zusätzliche Funktionalität? Wenn dies ein leistungsempfindlicher (Geschwindigkeits- / Speicher-Overhead) Teil Ihres Codes ist, wählen Sie zwischen AtomicReference/ AtomicFieldUpdater/ Unsafewo Sie dazu neigen, in Bezug auf Lesbarkeit und Risiko für Ihren Leistungsgewinn zu zahlen. Wenn dies kein sensibler Bereich ist, gehen Sie einfach für AtomicReference. Bibliotheksschreiber verwenden normalerweise eine Mischung dieser Methoden, abhängig von den Ziel-JDKs, den erwarteten API-Einschränkungen, den Speicherbeschränkungen usw.
Nitsan Wakart
quelle
7

JDK-Quellcode ist eine der besten Möglichkeiten, um solche Verwirrungen zu beantworten. Wenn Sie sich den Code in AtomicReference ansehen, wird eine volatie-Variable für die Objektspeicherung verwendet.

private volatile V value;

Wenn Sie also nur get () und set () für AtomicReference verwenden, ist dies wie die Verwendung einer flüchtigen Variablen. Wie andere Leser kommentierten, bietet AtomicReference zusätzliche CAS-Semantik. Entscheiden Sie also zuerst, ob Sie eine CAS-Semantik wünschen oder nicht, und verwenden Sie AtomicReference nur dann.

endlos
quelle
13
"JDK-Quellcode ist eine der besten Möglichkeiten, um solche Verwirrungen zu beantworten" => Ich stimme nicht unbedingt zu - der Javadoc (der Vertrag der Klasse) ist der beste Weg. Was Sie im Code finden, beantwortet die Frage für eine bestimmte Implementierung, aber der Code kann sich ändern.
Assylias
4
Zum Beispiel war diese Variable in Hashmap in JDK 6 flüchtig, aber in Java 7 nicht mehr flüchtig. Haben Sie Ihren Code auf die Tatsache gestützt, dass die Variable flüchtig war, wäre sie beim Ugrading Ihres JDK kaputt gegangen ... Zugegeben, das Beispiel ist anders, aber du verstehst, worum es geht.
Assylias
Ist das CAS eine Standardabkürzung?
Abbas
1
Vergleichen und tauschen =)
endlos
4

AtomicReferencebietet zusätzliche Funktionen, die eine einfache flüchtige Variable nicht bietet. Wenn Sie die API Javadoc gelesen haben, wissen Sie dies, aber es bietet auch eine Sperre, die für einige Vorgänge nützlich sein kann.

Sofern Sie diese zusätzliche Funktionalität nicht benötigen, empfehlen wir Ihnen, ein einfaches volatileFeld zu verwenden.

Peter Lawrey
quelle
Der Unterschied liegt also in ihrer Leistung. Wenn es keinen Unterschied gäbe, würden Sie niemals vorschlagen, einen über den anderen zu verwenden.
BT
Die Leistung ist ähnlich. Eine AtomicRefrence erhöht die Komplexität und die Speichernutzung.
Peter Lawrey
@BT Ein volatileFeld kann wie jedes reguläre Feld verwendet werden, während für den Zugriff auf den Wert in a Methoden und Methoden AtomicReferenceerforderlich sind. getset
David Harkness
0

Manchmal ist AtomicReference eine gute Wahl, selbst wenn Sie nur Gets und Sets verwenden:

Beispiel mit flüchtigen Bestandteilen:

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
  }
}

Die Implementierung mit AtomicReference bietet Ihnen eine kostenlose Copy-on-Write-Synchronisation.

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
  }
}

Man könnte sagen, dass Sie immer noch eine richtige Kopie haben könnten, wenn Sie ersetzen würden:

Status status = statusWrapper.get();

mit:

Status statusCopy = status;

Es ist jedoch wahrscheinlicher, dass der zweite in Zukunft versehentlich von jemandem während der "Code-Reinigung" entfernt wird.

nme
quelle