Nazywa się to efektem "przedwczesnej publikacji".
Mówiąc prościej, JVM może zmienić kolejność instrukcji programu (ze względu na wydajność), jeśli takie uporządkowanie nie narusza ograniczeń JMM.
Oczekujesz kod f = new FinalFieldExample();
biec jak:
1. utworzyć instancję FinalFieldExample
2. Przypisanie do 3 x
3. Nadać 4 do y
4. przywiąż utworzony obiekt do zmiennej f
Ale w kodzie przewidzianym, nic nie może zatrzymać JVM z instrukcją zmiany kolejności, więc może uruchomić kod jak:
1. utworzyć egzemplarz FinalFieldExample
2. Przypisanie do 3 x
3. przypisać surowe, nie w pełni zainicjowany obiektu zmiennej f
4. Przypisanie 4 Y
Jeżeli zmiana kolejności dzieje się w pojedynczym środowisko wątków, nawet tego nie zauważymy. To dlatego, że oczekujemy, że obiekty zostaną w pełni stworzone, zanim zaczniemy z nimi pracować, a JVM szanuje nasze oczekiwania. Co może się stać, jeśli kilka wątków uruchomi ten kod jednocześnie? W kolejnym przykładzie thread1 jest sposób writer()
i thread2 wykonania - metoda reader()
:
wątku 1: tworzenie wystąpienie FinalFieldExample
gwintu 1: Rola 3-X
gwintu 1: przypisanie surowe, nie w pełni zainicjowany obiektu zmiennej f
gwintu 2: odczytywanie f
, nie jest zerowy
gwintu 2: odczytywanie Fx jest 3
gwintu 2: odczytywanie fy jest nadal 0
gwintu 1: przypisanie 4 Y
Zdecydowanie nie jest dobra. Aby nie dopuścić do tego JVM, musimy podać dodatkowe informacje o programie. W tym konkretnym przykładzie, istnieje kilka sposobów, aby naprawić spójności pamięci:
- declare
y
jako final
zmienna. Spowoduje to efekt "freeze". W skrócie, ostateczne zmienne będą zawsze inicjowane w momencie, gdy uzyskasz do nich dostęp, , jeśli odwołanie do obiektu nie wyciekło podczas budowy.
- zadeklaruj
f
jako zmienną volatile
.Spowoduje to utworzenie "synchronization order" i naprawienie problemu. W skrócie, instrukcje nie mogą zostać zmienione poniżej volatile write i powyżej volatile read. Przypisanie do zmiennej f
jest zmiennym, co oznacza, że instrukcje new FinalFieldExample()
nie mogą zostać zmienione i wykonane po przypisaniu. Odczyt ze zmiennej f
jest zmiennym odczytem, więc odczyt f.x
nie może być wykonany przed nim. Kombinacja v-write i v-read nazywa się kolejnością synchronizacji i zapewnia pożądaną spójność pamięci.
Here to dobry blog, który może odpowiedzieć na wszystkie pytania dotyczące JMM.
Wiem, że to dotyczy maszyny JVM, która może zmieniać kolejność zapisów, ale nie znam szczegółów. : - | –
Ponieważ myślę, że nie przeczytałeś poprawnie jsr133, jest to wyraźnie wspomniane. "Nie pisz referencji do konstruowanego obiektu w miejscu, gdzie inny wątek może go zobaczyć przed zakończeniem konstruktora obiektu. Jeśli tak, to gdy inny obiekt zobaczy obiekt , ten wątek zawsze wyświetli poprawnie skonstruowaną wersję końcowych pól tego obiektu, ale w przypadku nieostatniego pola wątek może zobaczyć wartość domyślną. – Prashant
tak, to jest szalone. mam nadzieję, że naprawią to w java9. – ZhongYu