Kiedy muszę używać AtomicBoolean w Javie?

Odpowiedzi:

244

Gdy wiele wątków wymaga sprawdzenia i zmiany wartości logicznej. Na przykład:

if (!initialized) {
   initialize();
   initialized = true;
}

To nie jest bezpieczne dla wątków. Możesz to naprawić za pomocą AtomicBoolean:

if (atomicInitialized.compareAndSet(false, true)) {
    initialize();
}
Bozho
źródło
51
Nie wygląda to na przykład z prawdziwego świata - inny wątek może zobaczyć, truekiedy initialize()nie został ukończony. Działa to tylko wtedy, gdy inne wątki nie dbają o zakończenie initialize().
axtavt
6
@axtavt: Myślę, że jest to całkowicie poprawny przykład w świecie rzeczywistym, jeśli initializedjest po prostu używany, aby upewnić się, że initialize()metoda wywoła tylko jeden wątek . Oczywiście initializedfakt, że jest to prawda, nie oznacza, że ​​inicjalizacja zdecydowanie zakończyła się w tym przypadku, więc być może nieco inny termin byłby lepszy. Znowu zależy to od tego, do czego jest używany.
ColinD,
14
potrzebujesz 2 booleanów dla initStarted i initCompleted, następnie pierwszy zestaw wątków initStarted i wywołuje initialise (), pozostałe czekają, aż initCompleted będzie prawdziwy.
Martin
3
@Bozho - czyta i pisze, że pola boolowskie są atomowe, prawda ?, Teraz zmienność daje mi najnowszą wartość pola boolowskiego. Czy zatem nie byłoby tak volatile booleansamo jak AtomicBoolean?
TheLostMind
2
@Martin: Nie ma bezpośredniego sposobu, aby poczekać, aż wartość logiczna się spełni; potrzebujesz dodatkowych mechanizmów. Najbardziej sensownym podejściem jest użycie synchronizedbloku, w którym to przypadku nie potrzebujesz już AtomicBoolean, tylko volatile boolean. ( if(! this.initialized) { synchronized(this) { if(! this.initialized) { initialize(); this.initialized = true; } } }upewni się, że zadzwoni tylko jeden wątek initializei że wszystkie pozostałe wątki będą na niego czekać, pod warunkiem, że initializedjest zaznaczony volatile.)
ruakh 14.10.16
52

Oto notatki (z książki Briana Goetza ), które zrobiłem, które mogą ci pomóc

Klasy AtomicXXX

  • zapewniają nieblokującą implementację porównania i zamiany

  • Korzysta ze wsparcia zapewnianego przez sprzęt (instrukcja CMPXCHG na temat Intela) Gdy wiele wątków przepływa przez Twój kod korzystający z interfejsu API współbieżności atomowej, będą one skalowane znacznie lepiej niż kod korzystający z monitorów / synchronizacji na poziomie obiektu. Ponieważ mechanizmy synchronizacji Javy powodują, że kod czeka, gdy wiele krytycznych sekcji przebiega przez wiele wątków, znaczna część czasu procesora jest poświęcana na zarządzanie samym mechanizmem synchronizacji (oczekiwanie, powiadamianie itp.). Ponieważ nowy interfejs API wykorzystuje konstrukcje sprzętowe (zmienne atomowe) oraz algorytmy oczekiwania i blokowania w celu wdrożenia bezpieczeństwa wątków, dużo więcej czasu procesora spędza się na „robieniu rzeczy” niż na zarządzaniu synchronizacją.

  • nie tylko oferują lepszą przepustowość, ale także zapewniają większą odporność na problemy z żywotnością, takie jak impas i inwersja priorytetów.

Aravind Yarram
źródło
34

Istnieją dwa główne powody, dla których można użyć boolean atomowych. Po pierwsze, można go modyfikować, możesz przekazać go jako odniesienie i zmienić na przykład wartość powiązaną z samą wartością logiczną.

public final class MyThreadSafeClass{

    private AtomicBoolean myBoolean = new AtomicBoolean(false);
    private SomeThreadSafeObject someObject = new SomeThreadSafeObject();

    public boolean doSomething(){
         someObject.doSomeWork(myBoolean);
         return myBoolean.get(); //will return true
    }
}

oraz w klasie someObject

public final class SomeThreadSafeObject{
    public void doSomeWork(AtomicBoolean b){
        b.set(true);
    }
}

Co ważniejsze, jego wątek jest bezpieczny i może wskazywać deweloperom utrzymującym klasę, że oczekuje się, że zmienna ta zostanie zmodyfikowana i odczytana z wielu wątków. Jeśli nie używasz AtomicBoolean, musisz zsynchronizować używaną zmienną boolowską, deklarując jej zmienność lub synchronizując odczyt i zapis pola.

John Vint
źródło
4
Na miłość boską miało to tylko pokazać zmienność samego obiektu. Napisałem to specjalnie w celach demonstracyjnych.
John Vint,
Co więcej, jeśli to WSZYSTKO się działo, to tak, to zawsze zwróci się prawda
John Vint
To nie dowodzi, czy jest bezpieczne lub nie jest wątkowe. Mogę dokończyć fragmenty kodu, aby klasa była bardzo bezpieczna dla wątków, ale to tylko zabija mój punkt widzenia.
John Vint,
1
Myślę, że tylko Volatile to za mało. Pomyśl o sytuacji, w której dwa wątki, które odczytują i zapisują tę samą wartość bezpośrednio z pamięci głównej, nie ma synchronizacji między tymi wątkami - mogą wystąpić problemy ze współbieżnością.
Shay Tsadok
1
Masz rację, nie wystarczyłoby zestaw atomowy, a następnie sprawdź operacje, chociaż OP nie miał wystarczającego kontekstu, aby przyjąć to założenie. Powiedzieć, że zmienność może nie wystarczyć, zawsze jest prawdą, w zależności od sytuacji.
John Vint
18

AtomicBooleanKlasa daje wartość logiczną, które można aktualizować atomowo. Użyj go, gdy masz wiele wątków uzyskujących dostęp do zmiennej boolean.

Java.util.concurrent.atomic przegląd pakiet daje dobry opis na wysokim poziomie, co zajęcia w tym pakiecie zrobić i kiedy ich używać. Poleciłbym również książkę Java Concurrency in Practice autorstwa Briana Goetza.

Cameron Skinner
źródło
5

Fragment opisu pakietu

Pakiet java.util.concurrent.atomic opis: Mały zestaw klas, które obsługują bezpieczne dla wątków programowanie na pojedynczych zmiennych. [...]

Specyfikacje tych metod umożliwiają implementacjom stosowanie wydajnych instrukcji atomowych na poziomie maszyny, które są dostępne we współczesnych procesorach. [...]

Każda z instancji klas AtomicBoolean, AtomicInteger, AtomicLong i AtomicReference zapewnia dostęp i aktualizację pojedynczej zmiennej odpowiedniego typu. [...]

Efekty pamięci dla dostępu i aktualizacji atomów są ogólnie zgodne z regułami dla substancji lotnych:

  • get ma efekty pamięciowe odczytu zmiennej lotnej.
  • zbiór ma efekt pamięciowy zapisu (przypisania) zmiennej lotnej.
  • SłabyCompareAndSet czyta atomowo i warunkowo zapisuje zmienną, jest uporządkowany względem innych operacji pamięci na tej zmiennej, ale poza tym działa jak zwykła nieulotna operacja pamięci.
  • CompareAndSet i wszystkie inne operacje odczytu i aktualizacji, takie jak getAndIncrement, mają wpływ na pamięć zarówno odczytu, jak i zapisu zmiennych lotnych.
OscarRyz
źródło