Почему в double-checked locking поле instance обязано быть volatile?
new Singleton() — три шага: выделить память, выполнить конструктор, присвоить ссылку. Без volatile присвоение может опередить конструктор, и второй поток на первой проверке (вне synchronized) получит недостроенный объект.
- volatile запрещает это переупорядочивание и даёт happens-before
- Проще и надёжнее — holder-класс (ленивая инициализация загрузчиком) или enum
- В Java 1.4 и раньше volatile не спасал — модель памяти исправлена в Java 5
А ещё Локальная переменная уменьшает число volatile-чтений
Подробно
Разложи new Singleton() на три шага: выделить память, отработать конструктор, присвоить ссылку — операция не атомарна. Без volatile модель памяти разрешает переупорядочивание, при котором ссылка присваивается до завершения конструктора. Тогда второй поток на первой (небезопасной) проверке видит не нулевую ссылку, пропускает синхронизацию и получает частично инициализированный объект — с полями по умолчанию вместо реальных значений. volatile закрывает дыру, потому что запись в него happens-before последующего чтения и запрещает такое переупорядочивание. Сильное завершение — что в реальном коде проще взять enum-синглтон или static holder, где ленивость обеспечивают правила инициализации классов, и DCL писать вручную не нужно.
Пример
private static volatile Singleton instance;
static Singleton get() {
Singleton r = instance;
if (r == null) {
synchronized (Singleton.class) {
r = instance;
if (r == null) instance = r = new Singleton();
}
}
return r;
}
// проще:
class Holder { static final Singleton INSTANCE = new Singleton(); }Что хотят услышать
- Три шага создания и опасное переупорядочивание.
- Роль volatile.
- Альтернативы: holder и enum.
Частые ошибки
- Объяснять только через видимость, не упоминая переупорядочивание.
- Считать, что достаточно synchronized на второй проверке.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗volatile делает блок synchronized ненужным, и проверку можно проводить один раз
- ✗Без volatile поле кэшируется в регистре навсегда и второй поток создаст второй экземпляр
- ✗volatile нужен только для сериализации синглтона, чтобы при чтении из потока не появился второй объект
- ✓Иначе ссылка может стать видна раньше, чем отработал конструктор
Что спросят дальше
- Почему static holder ленив без единой блокировки?
- Что даёт final-поле в терминах безопасной публикации?
Разбор темы
- Java Memory Model: happens-before и volatile
Зачем нужна модель памяти, видимость и переупорядочивание, отношение happens-before, семантика volatile и final, гонки данных.
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.