Lotne referencje Java a AtomicReference

135

Czy jest jakaś różnica między volatileodwołaniem do obiektu i AtomicReferencena wypadek, gdybym po prostu użył get()i set()-methods z AtomicReference?

auramo
źródło

Odpowiedzi:

114

Krótka odpowiedź brzmi: nie.

Z java.util.concurrent.atomicdokumentacji pakietu. Cytować:

Efekty pamięciowe przy dostępach i aktualizacjach atomów generalnie są zgodne z zasadami dotyczącymi substancji lotnych:

  • getma wpływ na pamięć odczytu volatilezmiennej.
  • setma efekty pamięciowe pisania (przypisywania) volatilezmiennej.

Nawiasem mówiąc, ta dokumentacja jest bardzo dobra i wszystko wyjaśnione.


AtomicReference::lazySetjest nowszą wprowadzoną operacją (Java 6+), której semantyka jest nieosiągalna przez volatilezmienne. Więcej informacji znajdziesz w tym poście .

pgras
źródło
11
A dłuższa odpowiedź byłaby?
Julien Grenier
Zgoda. Potrzebujemy przynajmniej linku.
Julien Chastang
2
Link do dłuższej odpowiedzi: java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/atomic/…
Alex Siman
42

Nie, nie ma.

Dodatkową mocą zapewnianą przez AtomicReference jest metoda compareAndSet () i przyjaciele. Jeśli nie potrzebujesz tych metod, zmienne odwołanie zapewnia taką samą semantykę jak AtomicReference.set () i .get ().

Avi
źródło
14

Istnieje kilka różnic i kompromisów:

  1. Używanie AtomicReferenceget / set ma taką samą semantykę JMM jak zmienne pole (jak stwierdza javadoc), ale AtomicReferencejest opakowaniem wokół referencji, więc każdy dostęp do pola obejmuje dalszą pościg po wskaźnikach .

  2. Ilość pamięci jest mnożona (zakładając skompresowane środowisko OOP, co jest prawdą dla większości maszyn wirtualnych):

    • lotny ref = 4b
    • AtomicReference = 4b + 16b (12b nagłówek obiektu + 4b pole odniesienia)
  3. AtomicReferenceoferuje 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.Unsafejeśli lubisz biegać z nożyczkami. AtomicReferencejest zaimplementowany przy użyciu Unsafe.

Kiedy więc dobrze jest wybrać jedną z nich:

  • Potrzebujesz tylko dostać / ustawić? Trzymaj się zmiennego pola, najprostszego rozwiązania i najniższego narzutu.
  • Potrzebujesz dodatkowej funkcjonalności? Jeśli jest to część kodu wrażliwa na wydajność (szybkość / obciążenie pamięci), wybierz AtomicReference/ AtomicFieldUpdater/ Unsafegdzie 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.
Nitsan Wakart
źródło
7

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.

nieskończony
źródło
13
"Kod źródłowy JDK jest jednym z najlepszych sposobów rozwiązywania takich nieporozumień" => Niekoniecznie się z tym zgadzam - javadoc (który jest kontraktem klasy) jest najlepszym sposobem. To, co znajdziesz w kodzie, odpowiada na pytanie dotyczące konkretnej implementacji, ale kod może się zmienić.
assylias
4
Na przykład ta zmienna w hashmap była niestabilna w JDK 6, ale nie jest już ulotna w Javie 7. Czy oparłeś swój kod na fakcie, że zmienna była niestabilna, zepsułaby się podczas uaktualniania JDK ... Prawdą jest, że przykładem jest inaczej, ale o co chodzi.
asylias
Czy to CAS jest jakimś standardowym skrótem?
abbas
1
Porównaj i zamień =)
niekończące się
4

AtomicReferencezapewnia 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 volatilepola.

Peter Lawrey
źródło
Zatem różnica polega na ich działaniu. Gdyby nie było różnicy, nigdy nie sugerowałbyś używania jednego nad drugim.
BT,
Wydajność jest bardzo podobna. AtomicRefrence zwiększa złożoność i zużycie pamięci.
Peter Lawrey
@BT volatilePola można używać jak każdego zwykłego pola, podczas gdy dostęp do wartości w an AtomicReferencewymaga przejścia geti setmetod.
David Harkness,
0

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

nme
źródło