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 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.
volatile. Pytanie jest o volatile booleanvs AtomicBoolean.
volatile booleanto 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)
booleanvar, powinniśmy wybrać volatile boolean.
Nie można tego zrobić compareAndSet, getAndSetjako operacja atomowa lotnymi Boolean (o ile oczywiście go zsynchronizować).
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).
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.
Atomic*Klasa okłady volatilepole.
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
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ć.
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 #1i 2#, możesz otrzymać zły wynik. AtomicBooleanmetody unikają tego problemu, wykonując kroki #1i #2atomowo.
Oba mają tę samą koncepcję, ale w boolean atomowy zapewni atomowość operacji w przypadku, gdy nastąpi zmiana procesora między nimi.