2013-06-18 11 views
5

Poniżej fragment kodu jest z Efektywna Java 2nd Edition Blokada z podwójnym zatwierdzeniemCzy to jest lepsza wersja dokładnie sprawdzić Blokowanie bez narzutu lotnej i synchronizacji

// dwukrotnie sprawdzić idiom dla leniwej inicjalizacji instancji pola

private volatile FieldType field; 

FieldType getField() { 
    FieldType result = field; 
    if (result == null) { // First check (no locking) 
     synchronized(this) { 
      result = field; 
      if (result == null)// Second check (with locking) 
       field = result = computeFieldValue(); 
     } 
    } 
    return result; 
} 

Z tego, co wiem, głównym problemem z funkcją Double Checked Locking jest zmiana kolejności wewnątrz drugiego sprawdzania, tak aby inny wątek mógł zobaczyć wartości pola/wyniku, które mogą pozostać w trakcie wykonywania. Aby tego uniknąć, sprawiamy, że pole widzenia staje się zmienne, a widoczność i porządek są gwarantowane.

Ale można to osiągnąć przez poniższym kodzie również

private FieldType field; // non volatile 
private volatile boolean fence = false; 

FieldType getField() { 
    if (field == null) { // First check (no locking) // no volatile read 
     synchronized(this) { // inside synch block no problem of visibilty will latest   //value of field 
      if (field == null) {// Second check (with locking) 
       Object obj = computeFieldValue(); 
      fence = true; // any volatile write will take. this will make sure statements are //not reorder with setting field as non null. 
      field = (FieldType)obj; // this will be only set after computeFieldValue has been //completed fully 
      } 
     } 
    } 
    return field; 
} 

Więc po inicjalizacji, kiedy zostały zrobione, to nie wątek będzie musiał lotnych odczytu lub synchronizacji napowietrznych. Proszę Sprawdź, czy moje założenia są poprawne, czy nie?

+0

Czy blokowanie z podwójną kontrolą nie jest obecnie uważane za coś złego? http://en.wikipedia.org/wiki/Double-checked_locking – Bathsheba

+0

Jest jeden prosty sposób, aby tego uniknąć: zmień kod tak, aby _nie_ wymagał podwójnego sprawdzenia blokowania! W (100 - epsilon)% przypadków jest to wykonalne, i co należy zrobić w każdym razie. – fge

Odpowiedz

3

Nie ma sposobu, aby uzyskać "tanie" podwójne sprawdzanie blokowania w czystym JMM; coś musi dać.

Twoje rozwiązanie nie działa, ponieważ zmienny zapis może zostać ponownie uporządkowany przy użyciu następującej zwykłej czynności. Zobacz jsr133 cookbook dla dozwolonej zmiany kolejności w modelu tzw. "Motelu na karku". "Motel Roach" jest silniejszym modelem niż JMM, więc jeśli twoje rozwiązanie zawodzi w motelu karakanowym, to zawiedzie w JMM.

roach motel model 
reordering between a normal action and a volatile/monitor action 

    --        <-- 
    |         | 
    | VolatileLoad/MonitorEnter | forbidden 
    |         | 
    --> allowed      -- 


    --> allowed      -- 
    |         | 
    | VolatileStore/MonitorExit | forbidden 
    |         | 
    --        <-- 

Jest sposób powstrzymać zamianom dwóch zwykłych działań w „karaluch motel” model

(1) action#1 
(2) volatile write 
(3) volatile read 
(4) action#4 

(1) nie może być przegrupowana do (2) i (4) nie może być następującą kolejność (3), dlatego (1) i (4) nie można zmienić.

Należy jednak pamiętać, że model "karalucha" jest silniejszym modelem niż JMM. Nie można mieć pewności, że maszyna JVM jest zgodna z modelem motelu typu "Roach". Dla konkretnego przykładu:

action#1 
synchronized(new Object()){} 
synchronized(new Object()){} 
action#4 

według motelu "Płoć", działanie nr 1 i działanie nr 4 nie może być ponownie zamówione; jednak JVM może zgodnie z prawem (dozwolonym przez JMM) usunąć dwa bloki synchronizacji, a następnie zmienić kolejność pozostałych dwóch działań.

+0

dzięki za wspaniałe informacje – veritas

+1

możesz podać dowolny materiał referencyjny, w którym możemy znaleźć więcej informacji o JMM, który jest słabszy niż model motelowy – veritas

8

W JLS (Section 17.4.5) stany:

"Zapis do pola lotnej (§8.3.1.4) dzieje, zanim każda kolejna odczytu tej dziedzinie."

Nie czyta zmiennej fence po to jest aktualizowany, więc nie ma „się dzieje, zanim” związek pomiędzy gwintem, który aktualizuje fence oraz wszelkich drugiego wątku. Oznacza to, że drugi wątek nie gwarantuje zaktualizowania zmiennej field dokonanej przez pierwszy wątek.

W skrócie, twój "ulepszony" kod jest złamaną implementacją podwójnie sprawdzanego blokowania.

+0

+1 Wadliwa implementacja podwójnie sprawdzanego blokowania, które jest uszkodzonym konstruktem. :-) – Gray

+0

@Gray - podwójnie sprawdzane blokowanie to zły pomysł ... ale * może * być poprawnie zaimplementowane ... używając Java 1.5 i późniejszych. –

+0

Oczywiście, ale zazwyczaj jest bardziej skomplikowany niż 2 kontrole. :-) Oto dobry przykład na to, żeby to działało: http://stackoverflow.com/a/17112561/179850 – Gray

0

Według źródeł, którym ufam, wydaje się, że jest to działający i bezpieczny kod. Zapis zmiennej lotniczej musi wymuszać zapis wszystkich innych zmiennych i nie można go zmienić ani na lotność, ani na normalne przypisania.

Powiązane problemy