2011-08-01 10 views

Odpowiedz

7

Obawy dotyczące współbieżności nie ulegają awarii, ale jaka wersja danych widzisz.

  • jeśli zmienna dzielona jest napisane atomowo, jest to możliwe dla jednego (czytelnik) gwint odczytać wartość nieświeże, kiedy myśli swoją (pisarz) gwint uaktualnił zmienną. Możesz użyć niestabilnych słów kluczowych, aby zapobiec odczytywaniu wątków przez czytelnika z nieaktualnych wartości w tej sytuacji.

  • jeśli operacja zapisu nie jest atomowy (na przykład, jeśli jest to kompozyt przedmiotem pewnego rodzaju i piszesz bitów nim w czasie, podczas gdy inne wątki to teoretycznie może być czytanie go), a następnie swoją troską również być może niektóre wątki czytnika mogą zobaczyć zmienną w niespójnym stanie. Aby temu zapobiec, zablokuj dostęp do zmiennej podczas pisania (wolno) lub upewnij się, że piszesz atomowo.

3

Należy pamiętać, że nie jest lotny atomowej, co oznacza, że ​​podwójne i długi, które wykorzystują 64 bity mogą być odczytywane w niespójnym stanie, w którym 32 bitów jest stara wartość i 32 bitów jest nowa wartość. Również zmienne tablice nie powodują, że wpisy tablicy są niestabilne. Zalecane jest używanie klas z java.util.concurrent.

+1

I nie był świadomy, że podwaja i tęskni można zobaczyć w niespójnym stanie. Czy ma to również zastosowanie do (64-bitowych) odniesień w systemie 64-bitowym i oznacza, że ​​można uzyskać nieprawidłowe odwołanie? –

+0

Niespójne długie i podwójne z powodu odblokowanych zapisów są powszechnie znane jako "długie łzawienie". Odniesienia do obiektów wspierane przez wskaźnik 64-bitowy w jvm 64-bitowym NIE podlegają temu problemowi. Oznacza to, że "zapis" odniesienia do obiektu jest zawsze atomowy. –

+4

Lotne wartości 'long' i' double' ** są atomowe **, w przeciwieństwie do nielotnych wartości 'long' i' double' (które mogą być nieatomowe). Inne prymitywne wartości są atomowe, nawet jeśli są nielotne. –

4

Prosta odpowiedź brzmi: tak, potrzebna jest synchronizacja.

Jeśli kiedykolwiek napiszesz na pole i odczytasz je z dowolnego miejsca bez jakiejkolwiek synchronizacji, Twój program może zobaczyć niespójny stan i prawdopodobnie jest nieprawidłowy. Twój program się nie zawiesza, ale może zobaczyć stary lub nowy lub (w przypadku długich i podwójnych) połowę starych i pół nowych danych.

Kiedy mówię "jakąś formę synchronizacji", bardziej precyzyjnie mam na myśli coś, co tworzy relację "dzieje się przed" (czyli barierę pamięci) między lokalizacjami zapisu i odczytu. Klasy synchronizacji lub java.util.concurrent.lock są najbardziej oczywistym sposobem na stworzenie takiej rzeczy, ale wszystkie zbieżne kolekcje zwykle zapewniają podobne gwarancje (sprawdź javadoc, aby mieć pewność). Na przykład zrobienie wstawienia i podjęcia współbieżnej kolejki spowoduje utworzenie relacji dzieje się przed.

Oznaczenie pola jako lotnego uniemożliwia dostrzeżenie niespójnych odniesień (długotrwałe rozdarcie) i gwarantuje, że wszystkie wątki "zobaczą" zapis. Ale zmienne zapisy/odczyty nie mogą być łączone z innymi operacjami w większych jednostkach atomowych. Klasy Atomowe obsługują typowe operacje combo, takie jak porównywanie i ustawianie lub odczytywanie i zwiększanie. Synchronizacja lub inne synchronizatory java.util.concurrent (Cyclicator itp.) Lub blokady powinny być używane w przypadku większych obszarów wyłączności.

Odstąpienie od prostego tak, są przypadki, które są bardziej "nie, jeśli naprawdę wiesz, co robisz". Dwa przykłady:

1) Szczególny przypadek pola, które jest ostateczne i napisane TYLKO podczas budowy. Jednym z przykładów jest zapełnianie wstępnie obliczonej pamięci podręcznej (myślę o mapie, w której klucze są dobrze znane, a wartości są wstępnie obliczonymi wartościami pochodnymi). Jeśli zbudujesz to w polu przed budową, a pole jest ostateczne i nigdy nie napiszesz do niego później, koniec konstruktora wykonuje "ostateczne zamrożenie pola", a kolejne odczyty NIE wymagają synchronizacji.

2) Przypadek wzoru "racy single check" opisanego w efektywnej Java. Przykładem kanonicznym jest java.lang.String.hashCode(). Łańcuch ma pole mieszające, które jest leniwie obliczane przy pierwszym wywołaniu funkcji hashCode() i buforowane do pola lokalnego, które NIE jest zsynchronizowane. Zasadniczo, wiele wątków może ścigać się, aby obliczyć tę wartość i ustawić inne wątki, ale dlatego, że jest strzeżone przez dobrze znanego wartownika (0) i zawsze oblicza identyczną wartość (więc nie obchodzi nas, który wątek "wygrywa" lub czy wielokrotny do), to faktycznie gwarantuje, że jest w porządku.

Dłuższy odniesienia (napisany przeze mnie): http://refcardz.dzone.com/refcardz/core-java-concurrency