Kiedy korzystamy AtomicReference
?
Czy konieczne jest tworzenie obiektów we wszystkich programach wielowątkowych?
Podaj prosty przykład, w którym należy użyć AtomicReference.
źródło
Kiedy korzystamy AtomicReference
?
Czy konieczne jest tworzenie obiektów we wszystkich programach wielowątkowych?
Podaj prosty przykład, w którym należy użyć AtomicReference.
Odniesienia atomowego należy używać w ustawieniu, w którym należy wykonywać proste operacje atomowe (tj. Bezpieczne dla wątków , nietrywialne) na odwołaniu, dla których synchronizacja na podstawie monitorowania nie jest odpowiednia. Załóżmy, że chcesz sprawdzić, czy określone pole jest wyświetlane tylko wtedy, gdy stan obiektu pozostaje taki, jak podczas ostatniego sprawdzania:
AtomicReference<Object> cache = new AtomicReference<Object>();
Object cachedValue = new Object();
cache.set(cachedValue);
//... time passes ...
Object cachedValueToUpdate = cache.get();
//... do some work to transform cachedValueToUpdate into a new version
Object newValue = someFunctionOfOld(cachedValueToUpdate);
boolean success = cache.compareAndSet(cachedValue,cachedValueToUpdate);
Ze względu na semantykę odniesień atomowych możesz to zrobić, nawet jeśli cache
obiekt jest współużytkowany przez wątki, bez użycia synchronized
. Zasadniczo lepiej jest używać synchronizatorów lub java.util.concurrent
frameworka niż goły, Atomic*
chyba że wiesz, co robisz.
Dwie doskonałe referencje dotyczące martwych drzew, które wprowadzą Cię w ten temat:
Zauważ, że (nie wiem, czy to zawsze było prawdą) przypisanie referencji (tj. =
) Samo w sobie jest atomowe (aktualizowanie prymitywnych 64-bitowych typów takich jak long
lub double
nie może być atomowe; ale aktualizacja referencji jest zawsze atomowa, nawet jeśli jest 64-bitowa ) bez jawnego użycia Atomic*
.
Patrz specyfikacja języka Java 3ed, rozdział 17.7 .
AtomicReference
, powinieneś zaznaczyć zmienną,volatile
ponieważ chociaż środowisko wykonawcze gwarantuje, że przypisanie referencji jest niepodzielne, kompilator może przeprowadzić optymalizacje przy założeniu, że zmienna nie była modyfikowana przez inne wątki.AtomicReference
”; jeśli są przy użyciu go, a następnie moja rada to iść w kierunku przeciwnym i oznaczyć gofinal
tak kompilator może odpowiednio zoptymalizować.Odwołanie atomowe jest idealne do użycia, gdy trzeba udostępnić i zmienić stan niezmiennego obiektu między wieloma wątkami. To bardzo gęste stwierdzenie, więc trochę je podzielę.
Po pierwsze, niezmienny obiekt to obiekt, który nie ulega zmianie po budowie. Często metody niezmiennego obiektu zwracają nowe wystąpienia tej samej klasy. Niektóre przykłady obejmują klasy opakowań Long i Double, a także String, żeby wymienić tylko kilka. (Według Programowania współbieżności na niezmiennych obiektach JVM są krytyczne elementy współczesnej współbieżności).
Następnie, dlaczego AtomicReference jest lepszy niż zmienny obiekt do dzielenia się tą wspólną wartością. Prosty przykład kodu pokaże różnicę.
Za każdym razem, gdy chcesz zmodyfikować ciąg, do którego odwołuje się to zmienne pole w oparciu o jego bieżącą wartość, najpierw musisz uzyskać blokadę tego obiektu. Zapobiega to pojawianiu się w międzyczasie jakiegoś innego wątku i zmianie wartości w środku nowego łączenia łańcuchów. Następnie, gdy wznowisz wątek, zablokujesz pracę drugiego wątku. Ale szczerze mówiąc, ten kod będzie działał, wygląda na czysty i uszczęśliwiłby większość ludzi.
Niewielki problem. To jest wolne. Zwłaszcza jeśli istnieje spory o obiekt blokady. Dzieje się tak, ponieważ większość blokad wymaga wywołania systemowego OS, a Twój wątek blokuje się i zostaje wyłączony kontekstowo z procesora, aby zrobić miejsce dla innych procesów.
Inną opcją jest użycie AtomicRefrence.
Dlaczego to jest lepsze? Szczerze mówiąc, ten kod jest trochę mniej czysty niż wcześniej. Ale w AtomicRefrence dzieje się coś naprawdę ważnego, to jest porównywanie i zamiana. Jest to instrukcja pojedynczego procesora, a nie wywołanie systemu operacyjnego, które powoduje zmianę. To jest pojedyncza instrukcja dotycząca procesora. A ponieważ nie ma blokad, nie ma przełącznika kontekstu w przypadku blokowania, co oszczędza jeszcze więcej czasu!
W przypadku AtomicReferences haczykiem nie jest wywołanie .equals (), lecz porównanie == oczekiwanej wartości. Upewnij się więc, że oczekiwany jest rzeczywisty obiekt zwracany z pętli get.
źródło
worked
aby uzyskać tę samą semantykę.Oto przypadek użycia AtomicReference:
Rozważ tę klasę, która działa jako zakres liczb i wykorzystuje indywidualne zmienne AtmomicInteger do utrzymania dolnej i górnej granicy liczb.
Zarówno setLower, jak i setUpper są sekwencjami sprawdzającymi, a następnie działającymi, ale nie używają wystarczającego blokowania, aby uczynić je atomowymi. Jeśli zakres liczb zawiera (0, 10), a jeden wątek wywołuje setLower (5), podczas gdy inny wątek wywołuje setUpper (4), przy pewnym niefortunnym taktowaniu oba przejdą testy w seterach i zostaną zastosowane obie modyfikacje. W rezultacie zakres ma teraz (5, 4) nieprawidłowy stan. Tak więc, podczas gdy leżące u podstaw AtomicIntegers są bezpieczne dla wątków, klasa złożona nie jest. Można to naprawić za pomocą AtomicReference zamiast pojedynczych AtomicInteger dla górnej i dolnej granicy.
źródło
Możesz zastosować AtomicReference podczas stosowania optymistycznych blokad. Masz obiekt współdzielony i chcesz go zmienić z więcej niż jednego wątku.
Ponieważ inny wątek mógł go zmodyfikować i / lub może modyfikować między tymi 2 krokami. Musisz to zrobić w operacji atomowej. tutaj może pomóc AtomicReference
źródło
Oto bardzo prosty przypadek użycia i nie ma nic wspólnego z bezpieczeństwem wątków.
Aby udostępnić obiekt między wywołaniami lambda, dostępna
AtomicReference
jest opcja :Nie twierdzę, że jest to dobry projekt lub cokolwiek (to tylko trywialny przykład), ale jeśli masz przypadek, w którym musisz współdzielić obiekt między wywołaniami lambda,
AtomicReference
jest to opcja.W rzeczywistości możesz użyć dowolnego obiektu zawierającego odniesienie, nawet kolekcji zawierającej tylko jeden element. Jednak AtomicReference jest idealnie dopasowany.
źródło
Nie będę dużo rozmawiać. Już moi szanowani koledzy wnieśli swój cenny wkład. Pełny działający kod na końcu tego bloga powinien usunąć wszelkie nieporozumienia. Chodzi o rezerwację miejsca filmowego w małym programie w scenariuszu wielowątkowym.
Niektóre ważne elementarne fakty są następujące. 1> Różne wątki mogą rywalizować tylko na przykład o statyczne zmienne składowe w przestrzeni sterty. 2> Lotne odczytywanie lub zapisywanie jest całkowicie atomowe i szeregowane / odbywa się wcześniej i odbywa się wyłącznie z pamięci. Mówiąc to, mam na myśli, że każdy odczyt nastąpi po poprzednim zapisie w pamięci. I każdy zapis będzie następował po poprzednim odczycie z pamięci. Tak więc każdy wątek pracujący z niestabilną zawsze będzie widział najbardziej aktualną wartość. AtomicReference używa tej właściwości lotnej.
Oto niektóre z kodu źródłowego AtomicReference. AtomicReference odnosi się do odwołania do obiektu. To odwołanie jest zmienną zmienną składową w instancji AtomicReference, jak poniżej.
get () zwraca po prostu ostatnią wartość zmiennej (tak jak substancje lotne robią to w „zdarzeniu przed”).
Oto najważniejsza metoda AtomicReference.
Metoda CompareAndSet (expect, update) wywołuje metodę CompareAndSwapObject () niebezpiecznej klasy Java. Ta metoda wywołania niebezpiecznego wywołuje wywołanie rodzime, które wywołuje pojedynczą instrukcję do procesora. „expect” i „update” odnoszą się do obiektu.
Jeśli i tylko wtedy, gdy zmienna elementu „instancja” AtomicReference odnosi się do tego samego obiektu jest określana przez „oczekiwanie”, „aktualizacja” jest teraz przypisywana do tej zmiennej instancji i zwracane jest „prawda”. W przeciwnym razie zwracana jest wartość false. Wszystko odbywa się atomowo. Żaden inny wątek nie może przechodzić między nimi. Ponieważ jest to operacja jednoprocesorowa (magia współczesnej architektury komputerowej), często jest szybsza niż użycie bloku synchronicznego. Pamiętaj jednak, że gdy wiele zmiennych wymaga aktualizacji atomowej, AtomicReference nie pomoże.
Chciałbym dodać pełny działający kod, który można uruchomić w środowisku Eclipse. Rozwiałoby to wiele zamieszania. Tutaj 22 użytkowników (wątki MyTh) próbuje zarezerwować 20 miejsc. Poniżej znajduje się fragment kodu, po którym następuje pełny kod.
Fragment kodu, w którym 22 użytkowników próbuje zarezerwować 20 miejsc.
Poniżej znajduje się pełny działający kod.
źródło
AtomicReference to elastyczny sposób na atomową aktualizację wartości zmiennej bez synchronizacji.
AtomicReference
obsługuje bezpieczne dla wątków programowanie na pojedynczych zmiennych.Istnieje wiele sposobów osiągnięcia bezpieczeństwa wątków dzięki współbieżnemu interfejsowi API wysokiego poziomu . Zmienne atomowe to jedna z wielu opcji.
Lock
obiekty obsługują idiomy blokujące, które upraszczają wiele jednoczesnych aplikacji.Executors
zdefiniuj interfejs API wysokiego poziomu do uruchamiania wątków i zarządzania nimi. Implementacje executorów dostarczone przez java.util.concurrent zapewniają zarządzanie pulą wątków odpowiednie dla aplikacji na dużą skalę.Współbieżne kolekcje ułatwiają zarządzanie dużymi kolekcjami danych i mogą znacznie zmniejszyć potrzebę synchronizacji.
Zmienne atomowe mają funkcje minimalizujące synchronizację i pomagające uniknąć błędów spójności pamięci.
Przykładowy kod z
AtomicReference
:Nie musisz używać
AtomicReference
we wszystkich programach wielowątkowych.Jeśli chcesz chronić pojedynczą zmienną, użyj
AtomicReference
. Jeśli chcesz strzec bloku kodu, użyj innych konstrukcji, takich jakLock
/synchronized
itp.źródło
Innym prostym przykładem jest modyfikacja bezpiecznego wątku w obiekcie sesji.
Źródło: http://www.ibm.com/developerworks/library/j-jtp09238/index.html
źródło