Этот вопрос относится к поведению старых версий Java и старых реализаций алгоритма двойной проверки блокировки
Более новые реализации используют volatile
и полагаются на слегка измененныеvolatile
семантика, поэтому они не нарушены.
Установлено, что присвоение полей всегда атомарно, за исключением полей long или double.
Но,когда я читаю объяснение того, почему блокировка двойной проверки нарушена, мне говорят, что проблема в операции присваивания:
// Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null) {
synchronized(this) {
if (helper == null) {
helper = new Helper();
}
}
}
return helper;
}
// other functions and members...
}
- Поток A замечает, что значение не инициализировано, поэтомуон получает блокировку и начинает инициализировать значение.
- Из-за семантики некоторых языков программирования, код, сгенерированный компилятором, может обновлять совместно используемую переменную, чтобы указывать на частично созданный объект до завершения Aвыполнение инициализации.
- Поток B замечает, что общая переменная была инициализирована (или она так выглядит),и возвращает его значение.Поскольку поток B считает, что значение уже инициализировано, он не получает блокировку.Если B использует объект до того, как вся инициализация, выполненная A, будет видна B (либо потому, что A еще не завершила его инициализацию, либо потому, что некоторые из инициализированных значений в объекте еще не перколированы в используемую память B (когерентность кэша))программа, скорее всего, потерпит крах.
(из http://en.wikipedia.org/wiki/Double-checked_locking).
Когда это возможно? Возможно ли, что на 64-битной операции назначения JVM не атомарная? Если нет, то ли «двойная проверка блокировки»"действительно сломан?