37

Widziałem kilka przykładów w java, gdzie synchronizują się na bloku kodu, aby zmienić pewną zmienną, podczas gdy zmienna ta została początkowo uznana za niestabilną. Widziałem to w przykładzie klasy singleton, gdzie zadeklarowali unikalną instancję jako zmienną i sychronizowali blok, który inicjuje tę instancję ... Moje pytanie brzmi: dlaczego deklarujemy to jako niestabilne, podczas gdy my je synchronizujemy, dlaczego potrzebujemy obu? czy żaden z nich nie jest wystarczający dla drugiego?dlaczego używasz lotnych z zsynchronizowanym blokiem?

public class someClass { 
volatile static uniqueInstance = null; 

public static someClass getInstance() { 
     if(uniqueInstance == null) { 
      synchronized(someClass.class) { 
       if(uniqueInstance == null) { 
        uniqueInstance = new someClass(); 
       } 
      } 
     } 
     return uniqueInstance; 
    } 

z góry dzięki.

+2

Co to jest "volatile static uniqueInstance = null;" ? –

Odpowiedz

17

Synchronizacja sama w sobie wystarczyłaby, gdyby pierwsza kontrola była w zsynchronizowanym bloku (ale tak nie jest i jeden wątek może nie widzieć zmian dokonanych przez inny, jeśli zmienna nie była niestabilna). Samotność sama w sobie nie wystarczy, ponieważ trzeba wykonać więcej niż jedną operację atomowo. Ale strzeż się! To, co tu masz, to tak zwane "podwójne sprawdzanie" - wspólny język, który niestety jest następujący: does not work reliably. Myślę, że zmieniło się to od wersji 1.6, ale nadal ten rodzaj kodu może być ryzykowny.

EDIT: gdy zmienna jest lotny, ten kod działa poprawnie, ponieważ JDK 5 (nie 6 jak pisałem wcześniej), ale to nie będzie działać zgodnie z oczekiwaniami pod JDK 1.4 lub starszej.

+2

mówisz, że Synchronizacja wystarczy, jeśli pierwsza kontrola była w zsynchronizowanym bloku ... ale ponownie dokonuję tego samego sprawdzenia po wprowadzeniu bloku synchronizacji, więc na pewno następny wątek zobaczy zaktualizowaną wartość zmiennej. – Dorgham

+0

Każdy wątek może zobaczyć inną migawkę, więc fakt, że jeden wątek odczytuje zmienną wewnątrz zsynchronizowanego bloku, nie oznacza, że ​​inny wątek odczytujący zmienną bez synchronizacji zobaczy ten sam stan. Stan będzie spójny między wątkami tylko wtedy, gdy wszyscy użyją właściwej synchronizacji. Jak już wspomniałem, 'volatile' dba o to, aby w twoim przypadku z Java 5 wzwyż (tj.' Volatile' i 'synchronized' nie tylko powodowało, że każda z nich miała barierę pamięci, ale również używała tej samej bariery). –

+2

Tak samo jak heads-up dla przyszłych czytelników: powyższy artykuł jest przestarzały i jak stwierdza edycja, technika działa dobrze na JDK 5, który został wydany prawie 10 lat temu. –

4

This post wyjaśnia ideę volatile.

Jest to również adresowane w pracach nadrzędnych, Java Concurrency in Practice.

Główną ideą jest to, że współbieżność obejmuje nie tylko ochronę wspólnego państwa, ale także widoczności tym stanie pomiędzy wątkami: to gdzie lotny przychodzi (Ten większy kontrakt jest określony przez Java Memory Model.)

6

to. używa podwójnie sprawdzanego blokowania, zauważ, że if(uniqueInstance == null) nie znajduje się w zsynchronizowanej części.

Jeśli uniqueInstance nie jest niestabilny, może zostać "zainicjowany" częściowo utworzonym obiektem, którego części nie są widoczne dla innego niż wątek wykonywany w bloku synchronized. volatile sprawia, że ​​jest to operacja wszystko albo nic w tym przypadku.

Jeśli nie masz zsynchronizowanego bloku, możesz skończyć z dwoma wątkami docierającymi do tego punktu w tym samym czasie.

if(uniqueInstance == null) { 
     uniqueInstance = new someClass(); <---- here 

Tworzysz 2 obiekty SomeClass, które pokonują cel.

Ściśle mówiąc, nie trzeba lotny, metoda mogło

public static someClass getInstance() { 
    synchronized(FullDictionary.class) { 
     if(uniqueInstance == null) { 
      uniqueInstance = new someClass(); 
      } 
     return uniqueInstance; 
    } 
} 

Ale to ponosi synchronizację i szeregowanie każdego wątku, który wykonuje getInstance().

+0

Zrobiłem synchronizację po instrukcji if, aby zmniejszyć koszt synchronizacji, więc moje pytanie jest, dlaczego muszę zrobić to zmienne, gdy dwukrotnie sprawdzam po wprowadzeniu bloku synchronizacji? Mam na myśli, że synchronizacja natychmiast zaktualizuje wszystkie współdzielone zmienne, więc następny wątek zobaczy zaktualizowaną wartość uniqueInstance – Dorgham

+1

@ user1262445 Jak już powiedziałem, bez lotności wątek może zobaczyć obiekt jako częściowo skonstruowany. Więc nigdy nie wejdzie do bloku zsynchronizowanego, ale zwróci częściowo skonstruowany obiekt, jeśli unikalna instancja nie jest niestabilna. To znaczy. masz 1 wątek nie wchodzący do bloku synchronizacji i może on zwrócić śmieci, ponieważ inny wątek znajduje się w środku zsynchronizowanego bloku. – nos

+0

Moje pytanie jest bez 'volititle', w rzeczy samej pierwsze' if (uniqueInstance == null) {'może zostać przekazane, mimo że zostało zainicjalizowane, ale po wejściu do zsynchronizowanego bloku, ocena miss zostanie naprawiona, ponieważ została zsynchronizowana więc 'if (uniqueInstance == null)' będzie fałszywe i nie zostanie utworzone żadne nowe wystąpienie, czyż nie? Więc nawet bez valitle kod będzie działał zgodnie z oczekiwaniami (tylko jedno samo wystąpienie zostało utworzone i zwrócone). Ale rzeczywiście ma gorszą wydajność? Czy mam rację? – Jaskey

0

Możesz synchronizować bez blokowania zsynchronizowanego. Nie jest konieczne użycie zmiennej lotnej w niej ... lotne aktualizuje jedną zmienną z pamięci głównej .. i zsynchronizowane Zaktualizuj wszystkie zmienne współdzielone, do których dostęp uzyskano z pamięci głównej. Dzięki czemu można go używać zgodnie z wymaganie..

-1

Moje dwa centy tutaj

Frist szybkie wyjaśnienie intuicji tego kodu

if(uniqueInstance == null) { 
     synchronized(someClass.class) { 
      if(uniqueInstance == null) { 
       uniqueInstance = new someClass(); 
      } 
     } 
    } 

Powodem sprawdza uniqueInstance == null dwukrotnie jest zmniejszenie narzutu wywołanie bloku, który jest zsynchronizowany relatywnie wolniej. Tak zwane podwójne sprawdzanie blokowania.

Po drugie, powód, dla którego jest używany zsynchronizowany, jest łatwy do zrozumienia, czyni dwie operacje wewnątrz zsynchronizowanego bloku atomowego.

Ostatni lotny modyfikator zapewnia, że ​​wszystkie wątki widzą tę samą kopię, więc pierwsze sprawdzenie poza zsynchronizowanym blokiem wyświetli wartość uniqueInstance w sposób "zsynchronizowany" z synchronizowanym blokiem. Bez niestabilnego modyfikatora jeden wątek może przypisać wartość do uniqueInstance, ale drugi wątek może go nie widzieć po pierwszym sprawdzeniu. (Chociaż drugi test go zobaczy)

Powiązane problemy