Antworten:
Kurze Antwort lautet: Nein.
Aus der java.util.concurrent.atomic
Paketdokumentation. Zitieren:
Die Memory-Effekte für Zugriffe und Aktualisierungen von Atomics folgen im Allgemeinen den Regeln für flüchtige Stoffe:
get
hat die Memory-Effekte des Lesens einervolatile
Variablen.set
hat die Memory-Effekte des Schreibens (Zuweisens) einervolatile
Variablen.
Diese Dokumentation ist übrigens sehr gut und alles wird erklärt.
AtomicReference::lazySet
ist eine neuere (Java 6+) Operation, deren Semantik durch volatile
Variablen nicht erreicht werden kann. Weitere Informationen finden Sie in diesem Beitrag .
Es gibt verschiedene Unterschiede und Kompromisse:
Die Verwendung eines AtomicReference
get / set hat dieselbe JMM-Semantik wie ein flüchtiges Feld (wie im Javadoc angegeben), aber das AtomicReference
ist ein Wrapper um eine Referenz, sodass jeder Zugriff auf das Feld eine weitere Zeigerjagd erfordert .
Der Speicherbedarf wird multipliziert (unter der Annahme einer komprimierten OOP-Umgebung, was für die meisten VMs gilt):
AtomicReference
= 4b + 16b (12b Objektkopf + 4b Referenzfeld)AtomicReference
bietet eine umfangreichere API als eine flüchtige Referenz. Sie können die API für die flüchtige Referenz mithilfe von a AtomicFieldUpdater
oder mit Java 9 a wiederherstellen VarHandle
. Sie können auch geradeaus greifen, sun.misc.Unsafe
wenn Sie gerne mit einer Schere laufen. AtomicReference
selbst wird mit implementiert Unsafe
.
Wann ist es also gut, einen über den anderen zu wählen:
AtomicReference
/ AtomicFieldUpdater
/ Unsafe
wo 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.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.
AtomicReference
bietet 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 volatile
Feld zu verwenden.
volatile
Feld kann wie jedes reguläre Feld verwendet werden, während für den Zugriff auf den Wert in a Methoden und Methoden AtomicReference
erforderlich sind. get
set
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.