Я читал эссе, в котором фактически говорится о двойной проверке блокировки, но я удивлен еще более принципиальным отказом в коде, представленном в качестве примеров. Там указано, что возможно, что инициализация экземпляров (т.е. Запись в переменные экземпляра, которые происходят до возвращения конструктора), может быть переупорядочена до того, как ссылка на экземпляр будет записана в общую переменную (статическое поле в следующий пример).
Верно ли, что при следующем определении класса Foo
, при выполнении одного потока Foo.initFoo();
и другого потока, выполняющего System.out.println(Foo.foo.a);
, второй поток может печатать 0
(вместо 1
или бросать NullPointerException
)?
class Foo {
public int a = 1;
public static Foo foo;
public static void initFoo() {
foo = new Foo();
}
public static void thread1() {
initFoo(); // Executed on one thread.
}
public static void thread2() {
System.out.println(foo.a); // Executed on a different thread
}
}
Из того, что я знаю о модели памяти Java (и моделях памяти на других языках), на самом деле меня не удивляет, что это возможно, но интуиция очень сильно голосовала за то, что она невозможна (возможно, потому, что задействована инициализация объекта и объект инициализация кажется настолько священным в Java).
Можно ли "исправить" этот код (т.е. он никогда не будет печатать 0
) без синхронизации в первом потоке?