Volatile boolean vs AtomicBoolean

244

Co robi AtomicBoolean, czego nie osiąga lotna wartość logiczna?

JeffV
źródło
16
Szukałem bardziej szczegółowej odpowiedzi na: „jakie są ograniczenia każdego z nich?”. Na przykład, jeśli jest flagą ustawioną przez jeden wątek i odczytaną przez jeden lub więcej innych, nie ma potrzeby użycia AtomicBoolean. Jednak, jak widzę z tymi odpowiedziami, jeśli wątek współużytkuje zmienną w wielu wątkach, które mogą pisać i działają na podstawie wyników ich odczytów, AtomicBoolean wprowadza operacje nieblokujące typu CAS. Właściwie uczę się tutaj sporo. Mamy nadzieję, że inni również skorzystają.
JeffV
lotna wartość logiczna będzie wymagała wyraźnej synchronizacji, aby poradzić sobie z warunkami wyścigu, innymi słowy, scenariusz taki jak aktualizacja zasobu współdzielonego (zmiana stanu) przez wiele wątków, np. licznik przyrostu / zmniejszenia lub zmiana wartości logicznej.
sactiw

Odpowiedzi:

99

Po prostu są zupełnie inne. Rozważ ten przykład liczby volatilecałkowitej:

volatile int i = 0;
void incIBy5() {
    i += 5;
}

Jeśli dwa wątki wywołują funkcję jednocześnie, imoże to być później 5, ponieważ skompilowany kod będzie nieco podobny do tego (z tym wyjątkiem, że nie można zsynchronizować int):

void incIBy5() {
    int temp;
    synchronized(i) { temp = i }
    synchronized(i) { i = temp + 5 }
}

Jeśli zmienna jest lotna, każdy dostęp atomowy do niej jest synchronizowany, ale nie zawsze jest oczywiste, co tak naprawdę kwalifikuje się jako dostęp atomowy. W przypadku Atomic*obiektu gwarantuje się, że każda metoda jest „atomowa”.

Tak więc, jeśli użyjesz AtomicIntegeri getAndAdd(int delta), możesz być pewien, że wynik będzie 10. W ten sam sposób, jeśli oba wątki jednocześnie negują booleanzmienną, AtomicBooleanmożesz być pewien, że później będzie miała oryginalną wartość, a nie volatile boolean, możesz.

Więc jeśli masz więcej niż jeden wątek modyfikujący pole, musisz uczynić je atomowym lub użyć jawnej synchronizacji.

Cel volatilejest inny. Rozważ ten przykład

volatile boolean stop = false;
void loop() {
    while (!stop) { ... }
}
void stop() { stop = true; }

Jeśli wątek jest uruchomiony, loop()a inny wątek go wywołuje stop(), możesz wpaść w nieskończoną pętlę, jeśli go opuścisz volatile, ponieważ pierwszy wątek może buforować wartość stop. Tutaj volatilesłuży jako wskazówka dla kompilatora, aby był nieco bardziej ostrożny przy optymalizacji.

Głowonóg
źródło
84
-1: podajesz przykłady, ale tak naprawdę nie wyjaśniasz różnicy między lotną a Atomicxxxx.
Jason S
70
Pytanie nie dotyczy volatile. Pytanie jest o volatile booleanvs AtomicBoolean.
dolmen
26
-1: pytanie konkretnie zadane o wartości logicznej, która jest wyjątkowym przypadkiem w porównaniu z innymi typami danych i powinna zostać wyjaśniona bezpośrednio.
John Haager
8
@ sgp15 Ma to związek z synchronizacją od wersji Java 5.
Man of One Way
6
Jeśli wartość logiczna jest odczytywana przez wiele wątków, ale zapisywana tylko przez jeden wątek, volatile booleanto wystarczy. Jeśli jest też wielu pisarzy, możesz potrzebować AtomicBoolean.
StvnBrkdll
263

Używam zmiennych ulotnych, gdy wspomniane pole jest AKTUALIZOWANE TYLKO przez jego wątek właściciela, a wartość jest odczytywana tylko przez inne wątki, możesz myśleć o tym jak o scenariuszu publikowania / subskrypcji, w którym jest wielu obserwatorów, ale tylko jeden wydawca. Jeśli jednak ci obserwatorzy muszą wykonać jakąś logikę na podstawie wartości pola, a następnie wypchnąć nową wartość, idę z atomowymi * zmiennymi lub blokadami lub zsynchronizowanymi blokami, cokolwiek najbardziej mi odpowiada. W wielu współbieżnych scenariuszach sprowadza się do uzyskania wartości, porównania jej z inną i aktualizacji, jeśli to konieczne, stąd metody CompareAndSet i getAndSet obecne w klasach Atomic *.

Sprawdź JavaDocs pakietu java.util.concurrent.atomic , aby uzyskać listę klas Atomic i doskonałe wyjaśnienie ich działania (właśnie dowiedziałem się, że nie zawierają blokad, więc mają przewagę nad blokadami lub blokami synchronicznymi)

teto
źródło
1
@ksl Myślę, że @to chcę chcieć opisać, że jeśli tylko jeden wątek zmodyfikuje booleanvar, powinniśmy wybrać volatile boolean.
znlyj
2
Doskonałe podsumowanie.
Ravindra babu
56

Nie można tego zrobić compareAndSet, getAndSetjako operacja atomowa lotnymi Boolean (o ile oczywiście go zsynchronizować).

nanda
źródło
6
To prawda, ale czy nie byłoby to dość rzadkim wymogiem dla wartości logicznej?
Robin
1
@ Robin zastanawia się nad użyciem go do kontrolowania leniwego wywołania metody inicjalizacji.
Ustaman Sangat
Myślę, że to jeden z głównych przypadków użycia.
fool4jesus
42

AtomicBooleanma metody, które wykonują swoje złożone operacje atomowo i bez konieczności używania synchronizedbloku. Z drugiej strony volatile booleanmoże wykonywać operacje złożone tylko wtedy, gdy jest to wykonywane w synchronizedbloku.

Efekty pamięciowe odczytu / zapisu volatile booleansą identyczne odpowiednio z metodami geti .setAtomicBoolean

Na przykład compareAndSetmetoda atomowo wykona następujące czynności (bez synchronizedbloku):

if (value == expectedValue) {
    value = newValue;
    return true;
} else {
    return false;
}

Dlatego compareAndSetmetoda pozwoli Ci napisać kod, który gwarantuje wykonanie tylko raz, nawet jeśli zostanie wywołany z wielu wątków. Na przykład:

final AtomicBoolean isJobDone = new AtomicBoolean(false);

...

if (isJobDone.compareAndSet(false, true)) {
    listener.notifyJobDone();
}

Gwarantuje to, że powiadomi nas tylko raz (zakładając, że żaden inny wątek nie ustawia AtomicBooleango falseponownie po ustawieniu na true).

Nam San
źródło
14

volatilesłowo kluczowe gwarantuje wcześniejszą relację między wątkami współdzielącymi tę zmienną. Nie gwarantuje to, że 2 lub więcej wątków nie będzie sobie przeszkadzać podczas uzyskiwania dostępu do tej zmiennej boolowskiej.

dhblah
źródło
14
Dostęp boolowski (jak w typie pierwotnym) w Javie jest atomowy. Zarówno czyta, jak i zadania. Więc żaden inny wątek nie „przerwie” operacji boolowskich.
Maciej Biłas
1
Przepraszam, ale jak to odpowiada na pytanie? Atomic*Klasa okłady volatilepole.
Gray,
Czy pamięci podręczne procesora nie są głównym czynnikiem powodującym niestabilność? Aby upewnić się, że odczytana wartość jest faktycznie tym, co zostało ostatnio ustawione
żartuje
8

Volatile boolean vs AtomicBoolean

Klasy Atomic * zawijają lotną prymityw tego samego typu. Ze źródła:

public class AtomicLong extends Number implements java.io.Serializable {
   ...
   private volatile long value;
   ...
   public final long get() {
       return value;
   }
   ...
   public final void set(long newValue) {
       value = newValue;
   }

Więc jeśli wszystko, co robisz, to zdobywanie i ustawianie Atomowej *, równie dobrze możesz zamiast tego mieć zmienne pole.

Co robi AtomicBoolean, czego nie osiąga lotna wartość logiczna?

Klasy Atomic * oferują metody, które zapewniają bardziej zaawansowane funkcje, takie jak incrementAndGet(), compareAndSet()i inne, które implementują wiele operacji (get / increment / set, test / set) bez blokowania. Właśnie dlatego klasy Atomic * są tak potężne.

Na przykład, jeśli wiele wątków używa następującego kodu ++, będą występować warunki wyścigu, ponieważ w ++rzeczywistości jest to: pobierz, zwiększ i ustaw.

private volatile value;
...
// race conditions here
value++;

Jednak następujący kod będzie działał bezpiecznie w środowisku wielowątkowym bez blokad:

private final AtomicLong value = new AtomicLong();
...
value.incrementAndGet();

Należy również zauważyć, że owijanie pola niestabilnego za pomocą klasy Atomic * jest dobrym sposobem na enkapsulację krytycznego zasobu współdzielonego z punktu widzenia obiektu. Oznacza to, że programiści nie mogą sobie po prostu poradzić z polem, zakładając, że nie jest to wspólne, prawdopodobnie wprowadzając problemy z polem ++; lub inny kod wprowadzający warunki wyścigu.

Szary
źródło
5

Jeśli istnieje wiele wątków uzyskujących dostęp do zmiennej na poziomie klasy, każdy wątek może przechowywać kopię tej zmiennej w swojej pamięci podręcznej wątków.

Zmiana zmiennej na zmienną uniemożliwi wątkom przechowywanie kopii zmiennej w pamięci podręcznej wątków.

Zmienne atomowe są różne i umożliwiają atomową modyfikację ich wartości.

Amol Gaikwad
źródło
4

Logiczny typ prymitywny jest atomowy dla operacji zapisu i odczytu, zmienny gwarantuje zasadę „zdarza się przed”. Więc jeśli potrzebujesz prostej metody get () i set (), nie potrzebujesz AtomicBoolean.

Z drugiej strony, jeśli trzeba zaimplementować pewne sprawdzenie przed ustawieniem wartości zmiennej, np. „Jeśli prawda, to ustaw na wartość fałsz”, to musisz wykonać tę operację również atomowo, w tym przypadku użyj funkcji porównawczej i innych metod dostarczonych przez AtomicBoolean, ponieważ jeśli spróbujesz zaimplementować tę logikę przy pomocy lotnych wartości logicznych, będziesz potrzebować synchronizacji, aby mieć pewność, że wartość nie zmieniła się między get a set.

użytkownik2660000
źródło
3

Zapamiętaj IDIOM -

CZYTAJ - MODYFIKUJ - NAPISZ to, czego nie da się osiągnąć przy pomocy lotnych

MoveFast
źródło
2
Krótkie, chrupiące i na temat. volatiledziała tylko w przypadkach, gdy wątek właściciela ma możliwość aktualizacji wartości pola, a inne wątki mogą tylko odczytać.
Chaklader Asfak Arefe
3

Jeśli masz tylko jeden wątek modyfikujący wartość logiczną, możesz użyćstop zmiennej wartości logicznej (zwykle robisz to, aby zdefiniować zmienną sprawdzaną w głównej pętli wątku).

Jeśli jednak masz wiele wątków modyfikujących wartość logiczną, powinieneś użyć AtomicBoolean. W przeciwnym razie poniższy kod nie jest bezpieczny:

boolean r = !myVolatileBoolean;

Ta operacja odbywa się w dwóch krokach:

  1. Wartość logiczna jest odczytywana.
  2. Wartość logiczna jest zapisywana.

Jeśli inny wątek zmodyfikuje wartość pomiędzy #1i 2#, możesz otrzymać zły wynik. AtomicBooleanmetody unikają tego problemu, wykonując kroki #1i #2atomowo.

Thibaut D.
źródło
-1

Oba mają tę samą koncepcję, ale w boolean atomowy zapewni atomowość operacji w przypadku, gdy nastąpi zmiana procesora między nimi.

Bismaya Mohapatra
źródło