Double-checked locking без volatile — баг, который не воспроизводится на тесте
Классический паттерн ленивой инициализации синглтона кандидаты часто пишут по памяти — и забывают одну деталь, которая всё ломает.
public class Singleton { private static Singleton instance;
public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
Без volatile на поле instance этот код может отдать другому потоку ссылку на частично инициализированный объект. Создание объекта — это не одна атомарная операция: выделяется память, вызывается конструктор, только потом полю присваивается ссылка. JIT и процессор могут переставить эти шаги местами, если между ними нет барьера памяти.
Поток B в первой проверке if (instance == null) видит ненулевую ссылку, потому что она уже присвоена, но конструктор ещё не дописал состояние объекта. B получает instance с полями в значениях по умолчанию вместо тех, что задал конструктор.
Почему это не ловится на тесте: на x86 с текущими JIT-оптимизациями переупорядочивание такого рода происходит редко, и однопоточный или слабонагруженный тест этого не покажет. Баг всплывает под нагрузкой, на другой архитектуре или после изменения версии JVM — то есть ровно там, где его сложнее всего диагностировать.
Дальше спросят: как исправить одним словом — добавить volatile к полю instance, это восстанавливает happens-before между записью в конструкторе и публикацией ссылки. Ещё спросят про альтернативы: holder-класс с ленивой инициализацией через static-блок или enum-синглтон, где корректность гарантирует сама JVM без ручной синхронизации.
Если double-checked locking — то только с volatile. Без него код компилируется, проходит тесты и падает в проде под нагрузкой, где такие вещи ловить дольше всего.
Тренажёр: 600 вопросов, мок с таймером, план повторов
senior·base — что спрашивают на самом деле
В этом посте были ссылки, но мы их удалили по правилам Сетки
· 19.08
в проде я уже лет пять не видел рукописный синглтон — spring или micronaut сами рулят скоупом, и весь dcl-паттерн просто не возникает. а если пишешь библиотеку без di, enum singleton из effective java закрывает вопрос одной строкой без единого synchronized. на собесе volatile красиво звучит, но это знание из категории «знаю, чтобы никогда не писать»
0
ответить
коммент скрыт — часть юзеров считает его токсичным или некорректным
коммент удалён