Почему статический синглтон - это простое и элегантное решение, позволяющее избежать "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. Тема 1 вызывает getInstance() метод и определяет, что singleton является null,

  2. Поток 1 входит в блок if, но перед созданием нового экземпляра прерывается потоком 2.

  3. Тема 2 вызывает getInstance() метод и определяет, что экземпляр null,

  4. Поток 2 входит в блок if и создает новый Helper объект и назначает переменную singleton к этому новому объекту.

  5. Поток 2 возвращает ссылку на объект Singleton.

  6. Поток 2 вытесняется потоком 1.

  7. Поток 1 начинается там, где остановился, и выполняетсяsingleton = new Helper(); что приводит к созданию другого объекта Singleton.

  8. Поток 1 возвращает этот объект.

Таким образом, мы заканчиваем двумя случаями. Лучший способ создать Singletons - использовать enum, как здесь ответили.

static Helper singleton = new Helper(); 

Здесь новый экземпляр Helper создается, когда класс загружен и singleton содержит ссылку на этот экземпляр. Вы можете получить подробный процесс инициализации в JLS 12.4.2.

Другие вопросы по тегам