Odpowiedzi:
Krótka odpowiedź brzmi: nie.
Z java.util.concurrent.atomic
dokumentacji pakietu. Cytować:
Efekty pamięciowe przy dostępach i aktualizacjach atomów generalnie są zgodne z zasadami dotyczącymi substancji lotnych:
get
ma wpływ na pamięć odczytuvolatile
zmiennej.set
ma efekty pamięciowe pisania (przypisywania)volatile
zmiennej.
Nawiasem mówiąc, ta dokumentacja jest bardzo dobra i wszystko wyjaśnione.
AtomicReference::lazySet
jest nowszą wprowadzoną operacją (Java 6+), której semantyka jest nieosiągalna przez volatile
zmienne. Więcej informacji znajdziesz w tym poście .
Istnieje kilka różnic i kompromisów:
Używanie AtomicReference
get / set ma taką samą semantykę JMM jak zmienne pole (jak stwierdza javadoc), ale AtomicReference
jest opakowaniem wokół referencji, więc każdy dostęp do pola obejmuje dalszą pościg po wskaźnikach .
Ilość pamięci jest mnożona (zakładając skompresowane środowisko OOP, co jest prawdą dla większości maszyn wirtualnych):
AtomicReference
= 4b + 16b (12b nagłówek obiektu + 4b pole odniesienia)AtomicReference
oferuje bogatsze API niż zmienne odniesienie. Możesz odzyskać API dla ulotnego odwołania, używając pliku AtomicFieldUpdater
, lub Java 9 a VarHandle
. Możesz też sięgnąć prosto, sun.misc.Unsafe
jeśli lubisz biegać z nożyczkami. AtomicReference
jest zaimplementowany przy użyciu Unsafe
.
Kiedy więc dobrze jest wybrać jedną z nich:
AtomicReference
/ AtomicFieldUpdater
/ Unsafe
gdzie zwykle płacisz za czytelność i ryzyko wzrostu wydajności. Jeśli nie jest to wrażliwy obszar, po prostu idź AtomicReference
. Twórcy bibliotek zazwyczaj używają kombinacji tych metod w zależności od docelowych zestawów JDK, oczekiwanych ograniczeń interfejsu API, ograniczeń pamięci i tak dalej.Kod źródłowy JDK jest jednym z najlepszych sposobów odpowiedzi na takie niejasności. Jeśli spojrzysz na kod w AtomicReference, używa on zmiennej volatie do przechowywania obiektów.
private volatile V value;
Więc oczywiście, jeśli zamierzasz po prostu użyć get () i set () na AtomicReference, jest to jak użycie zmiennej lotnej. Ale jak skomentowali inni czytelnicy, AtomicReference zapewnia dodatkową semantykę CAS. Więc najpierw zdecyduj, czy chcesz semantyki CAS, czy nie, a jeśli tylko chcesz, użyj AtomicReference.
AtomicReference
zapewnia dodatkową funkcjonalność, której nie zapewnia zwykła zmienna zmienna. Po przeczytaniu Javadoc API będziesz o tym wiedział, ale zapewnia również blokadę, która może być przydatna w niektórych operacjach.
Jeśli jednak nie potrzebujesz tej dodatkowej funkcjonalności, sugeruję użycie zwykłego volatile
pola.
volatile
Pola można używać jak każdego zwykłego pola, podczas gdy dostęp do wartości w an AtomicReference
wymaga przejścia get
i set
metod.
Czasami, nawet jeśli używasz tylko pobierań i zestawów, AtomicReference może być dobrym wyborem:
Przykład z niestabilnym:
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
}
}
Implementacja z AtomicReference zapewniłaby bezpłatną synchronizację kopiowania przy zapisie.
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
}
}
Można by powiedzieć, że nadal możesz mieć odpowiednią kopię, jeśli zastąpisz:
Status status = statusWrapper.get();
z:
Status statusCopy = status;
Jednak druga z nich jest bardziej prawdopodobna, że zostanie przypadkowo usunięta przez kogoś w przyszłości podczas „czyszczenia kodu”.