Двойная проверка шаблона блокировки: неисправен или нет?

Почему шаблон считается нарушенным? Мне это нравится? Есть идеи?

public static Singleton getInst() {
    if (instace == null) createInst();
    return instace;
}

private static synchronized createInst() {
     if (instace == null) {
         instace = new Singleton(); 
     }
}
9
задан Sean Patrick Floyd 2 September 2010 в 12:08
поделиться

4 ответа

На первый взгляд все выглядит нормально, но у этого метода есть много тонких проблем, и его обычно следует избегать. Например, рассмотрим следующую последовательность событий:

  1. Поток A замечает, что значение не инициализирован, поэтому он получает заблокировать и начать инициализацию ценность.
  2. Код, сгенерированный компилятором, разрешен. обновить общую переменную до указать на частично построенный объект до завершения A выполнение инициализации.
  3. Поток B замечает, что общий переменная была инициализирована (или так появляется) и возвращает свое значение. Поскольку поток B считает, что значение уже инициализирован, он не получить замок. Если Б использует объект перед всеми инициализация, выполненная A, видна B программа, скорее всего, выйдет из строя.

Этого можно было бы избежать, используя ключевое слово volatile для правильной обработки одноэлементных экземпляров

21
ответ дан 4 December 2019 в 06:00
поделиться

Проблема заключается в следующем: ваша JVM может изменить порядок вашего кода, а поля не всегда одинаковы для разных потоков. Взгляните на это: http://www.ibm.com/developerworks/java/library/j-dcl.html. Использование ключевого слова volatile должно исправить это, но оно не работает до версии java 1.5.

В большинстве случаев блокировка с одиночной проверкой более чем достаточно быстра, попробуйте следующее:

// single checked locking: working implementation, but slower because it syncs all the time
public static synchronized Singleton getInst() {
    if (instance == null) 
        instance = new Singleton();
    return instance;
}

Также взгляните на эффективную java, где вы найдете отличную главу по этой теме.

Подводя итог: Не выполняйте блокировку с двойной проверкой, есть идомы получше.

6
ответ дан 4 December 2019 в 06:00
поделиться

Я не знаю, сломано ли это, однако это не самое эффективное решение из-за довольно дорогой синхронизации. Лучшим подходом было бы использование «идиомы держателя инициализации по запросу», которая загружает ваш синглтон в память при первом запросе, как следует из названия, таким образом, ленивая загрузка. Самое большое преимущество, которое вы получаете с этой идиомой, заключается в том, что вам не нужно синхронизироваться, поскольку JLS гарантирует, что загрузка классов является последовательной.

Подробная запись в Википедии по этому вопросу: http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom

Еще одна вещь, которую следует иметь в виду, это то, что с появлением сред внедрения зависимостей, таких как Spring и Guice, класс экземпляры создаются и управляются этими контейнерами, и при желании они предоставят вам синглтон, поэтому не стоит ломать голову над этим, если только вы не хотите научиться идеям, лежащим в основе шаблона, что полезно. Также обратите внимание, что синглтоны, предоставляемые этими контейнерами IOC, являются синглтонами для каждого экземпляра контейнера, но обычно у вас будет один контейнер IOC для каждого приложения, поэтому это не становится проблемой.

7
ответ дан 4 December 2019 в 06:00
поделиться

Это не ответ на ваш вопрос (другие уже ответили), но я хочу рассказать вам о своем опыте работы с синглтонами/ленивыми инициализированными объектами:

В нашем коде было несколько синглетонов . Однажды нам пришлось добавить параметр конструктора в один синглтон и возникла серьезная проблема, потому что конструктор этого синглтона вызывался на геттере. Были только следующие возможные решения:

  • предоставить статический геттер (или другой синглтон) для объекта, который был необходим для инициализации этого синглтона,
  • передать объект для инициализации синглтона в качестве параметра для геттера или
  • получить избавиться от синглтона, передавая экземпляры.

Наконец, последний вариант был выходом. Теперь мы инициализируем все объекты при запуске приложения и передаем необходимые экземпляры (возможно, в виде небольшого интерфейса). Мы не пожалели об этом решении, потому что

  • зависимости фрагмента кода предельно ясны,
  • мы можем гораздо проще тестировать наш код, предоставляя фиктивные реализации требуемых объектов.
2
ответ дан 4 December 2019 в 06:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: