Почему статический синглтон - это простое и элегантное решение, позволяющее избежать "DCL"?
Когда нужен синглтон, является ли статическое поле элегантным решением?
class HelperSingleton {
static Helper singleton = new Helper();
public static Helper getInstance() {
return singleton;
}
}
Когда два потока получают доступ к getInstance
в то же время, есть ли шанс поле singleton
не инициализируется полностью? Или посмотрите значения по умолчанию для полей вспомогательного объекта, а не значения, установленные в конструкторе? Статический синглтон тоже ленивая инициализация?
Я имею в виду,static Helper singleton = new Helper();
это назначение атомное? И не вернет значения по умолчанию?
4 ответа
1) Когда поток обращается к статическому getInstance, первый раз загрузчик класса должен загрузить класс HelperSingleton, и он заблокирует другой поток перед загрузкой класса. Таким образом, присутствует неявная синхронизация. Дж. Блох "Эффективная Java". Элемент 71 современной виртуальной машины будет синхронизировать доступ к полю только для инициализации класса. Как только класс инициализирован, виртуальная машина исправит код так, чтобы последующий доступ к полю не включал никакого тестирования или синхронизации.
2) Ваш сингл ленив, потому что есть только одна точка доступа - getInstance. По запросу создается не только экземпляр, но и весь класс. Класс не будет инициализирован, пока не будет использован [JLS, 12.4.1].
Я думаю, что самое элегантное решение для синглтона это:
public enum Singleton {
INSTANCE;
public Singleton getInstance() {
return INSTANCE;
}
}
Использование шаблона синглтона несколько проблематично, потому что иногда вы не знаете, что у вас есть только один из них. Рассмотрим случай, например, когда вы используете загрузчики классов.
Этот вопрос (и другие), кстати, имеет подробное объяснение.
Посмотрите на http://en.wikipedia.org/wiki/Singleton_pattern
Есть много доступных стилей, объясняющих хорошее / плохое.
Если бы это было так:
class HelperSingleton {
static Helper singleton = null;
public static Helper getInstance() {
if(singleton == null) {
singleton = new Helper();
}
return singleton;
}
}
Здесь может быть возможность:
Тема 1 вызывает
getInstance()
метод и определяет, чтоsingleton
являетсяnull
,Поток 1 входит в блок if, но перед созданием нового экземпляра прерывается потоком 2.
Тема 2 вызывает
getInstance()
метод и определяет, что экземплярnull
,Поток 2 входит в блок if и создает новый
Helper
объект и назначает переменнуюsingleton
к этому новому объекту.Поток 2 возвращает ссылку на объект Singleton.
Поток 2 вытесняется потоком 1.
Поток 1 начинается там, где остановился, и выполняется
singleton = new Helper();
что приводит к созданию другого объекта Singleton.Поток 1 возвращает этот объект.
Таким образом, мы заканчиваем двумя случаями. Лучший способ создать Singletons - использовать enum, как здесь ответили.
static Helper singleton = new Helper();
Здесь новый экземпляр Helper
создается, когда класс загружен и singleton
содержит ссылку на этот экземпляр. Вы можете получить подробный процесс инициализации в JLS 12.4.2.