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?

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 .


11
A dłuższa odpowiedź byłaby?
— Julien Grenier

Zgoda. Potrzebujemy przynajmniej linku.
— Julien Chastang


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 ().


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.

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.


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.


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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.