Co robi AtomicBoolean, czego nie osiąga lotna wartość logiczna?
Co robi AtomicBoolean, czego nie osiąga lotna wartość logiczna?
Odpowiedzi:
Po prostu są zupełnie inne. Rozważ ten przykład liczby volatile
całkowitej:
volatile int i = 0;
void incIBy5() {
i += 5;
}
Jeśli dwa wątki wywołują funkcję jednocześnie, i
moż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 AtomicInteger
i 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ą boolean
zmienną, AtomicBoolean
moż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 volatile
jest 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 volatile
służy jako wskazówka dla kompilatora, aby był nieco bardziej ostrożny przy optymalizacji.
volatile
. Pytanie jest o volatile boolean
vs AtomicBoolean
.
volatile boolean
to wystarczy. Jeśli jest też wielu pisarzy, możesz potrzebować AtomicBoolean
.
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)
boolean
var, powinniśmy wybrać volatile boolean
.
Nie można tego zrobić compareAndSet
, getAndSet
jako operacja atomowa lotnymi Boolean (o ile oczywiście go zsynchronizować).
AtomicBoolean
ma metody, które wykonują swoje złożone operacje atomowo i bez konieczności używania synchronized
bloku. Z drugiej strony volatile boolean
może wykonywać operacje złożone tylko wtedy, gdy jest to wykonywane w synchronized
bloku.
Efekty pamięciowe odczytu / zapisu volatile boolean
są identyczne odpowiednio z metodami get
i .set
AtomicBoolean
Na przykład compareAndSet
metoda atomowo wykona następujące czynności (bez synchronized
bloku):
if (value == expectedValue) {
value = newValue;
return true;
} else {
return false;
}
Dlatego compareAndSet
metoda 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 AtomicBoolean
go false
ponownie po ustawieniu na true
).
volatile
sł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.
Atomic*
Klasa okłady volatile
pole.
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.
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.
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.
Zapamiętaj IDIOM -
CZYTAJ - MODYFIKUJ - NAPISZ to, czego nie da się osiągnąć przy pomocy lotnych
volatile
działa tylko w przypadkach, gdy wątek właściciela ma możliwość aktualizacji wartości pola, a inne wątki mogą tylko odczytać.
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:
Jeśli inny wątek zmodyfikuje wartość pomiędzy #1
i 2#
, możesz otrzymać zły wynik. AtomicBoolean
metody unikają tego problemu, wykonując kroki #1
i #2
atomowo.
Oba mają tę samą koncepcję, ale w boolean atomowy zapewni atomowość operacji w przypadku, gdy nastąpi zmiana procesora między nimi.