Синглтон в Яве - PullRequest
       13

Синглтон в Яве

5 голосов
/ 17 июля 2010

Я только что где-то прочитал следующий код:

public class SingletonObjectDemo {

    private static SingletonObjectDemo singletonObject;
    // Note that the constructor is private
    private SingletonObjectDemo() {
        // Optional Code
    }
    public static SingletonObjectDemo getSingletonObject() {
        if (singletonObject == null) {
            singletonObject = new SingletonObjectDemo();
       }
       return singletonObject;
    }
}

Мне нужно знать, для чего нужна эта часть:

if (singletonObject == null) {
    singletonObject = new SingletonObjectDemo();
}

Что если мы не будем использовать эту часть кода? Там все равно будет одна копия SingletonObjectDemo, зачем нам тогда этот код?

Ответы [ 7 ]

9 голосов
/ 17 июля 2010

При ленивой или активной инициализации

Оператор if является реализацией метода lazy initialization .

Более точная версия выглядит следующим образом:

private boolean firstTime = true;
private Stuff stuff;

public Stuff gimmeStuff() {
   if (firstTime) {
      firstTime = false;
      stuff = new Stuff();
   }
   return stuff;
}

Что происходит, так это то, что в самый первый раз gimmeStuff() вызывается, firstTime будет true, поэтому stuff будет инициализирован new Stuff(). При последующих вызовах firstTime будет false, поэтому new Stuff() больше не будет вызываться.

Таким образом, stuff инициализируется «лениво». На самом деле он не инициализируется до самого первого раза.

Смотри также


по безопасности потока

Надо сказать, что фрагмент не является потокобезопасным. Если имеется несколько потоков, то в некоторых условиях гонки new SingletonObjectDemo() может вызываться несколько раз.

Одним из решений является метод synchronized getSingletonObject(). Это, однако, приводит к накладным расходам синхронизации при ALL вызовах на getSingletonObject(). Затем для исправления этой проблемы используется так называемая идиома с двойной проверкой блокировки , но в Java эта идиома фактически не работает до J2SE 5.0 с введением ключевого слова volatile в новой модели памяти .

Само собой разумеется, что правильное применение шаблона синглтона не является тривиальной вещью.

Смотри также

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


Effective Java 2nd Edition

Вот что говорит книга по этим предметам:

Item 71: Используйте ленивую инициализацию разумно

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

При наличии нескольких потоков ленивая инициализация сложна. Если два или более потоков совместно используют поле с отложенной инициализацией, очень важно использовать некоторую форму синхронизации, иначе могут возникнуть серьезные ошибки.

В большинстве случаев нормальная инициализация предпочтительнее ленивой инициализации.

Элемент 3: Применить свойство singleton с помощью частного конструктора или типа enum

Начиная с версии 1.5. Существует третий подход к реализации синглетонов. Просто создайте тип enum с одним элементом. [...] Этот подход функционально эквивалентен полевому подходу public, за исключением того, что он более лаконичен, предоставляет механизм сериализации бесплатно и обеспечивает железную гарантию от множественных экземпляров, даже в условиях сложной сериализации или основанной на отражении атаки.

[...] Лучше всего реализовать одноэлементный тип перечисления.

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

On enum Singleton / Java-реализация:

О достоинствах и альтернативах одноэлементного шаблона:

5 голосов
/ 17 июля 2010

Этот класс имеет поле SingletonObjectDemo singletonObject, которое содержит экземпляр singleton.Теперь есть две возможные стратегии -

1 - вы хотите инициализировать объект с помощью объявления -

private static SingletonObjectDemo singletonObject = new SingletonObjectDemo();

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

2 - Вы выполняете ленивую инициализацию объекта, т.е.выполнен вызов getSingletonObject() -

// note that this initializes the object to null by default
private static SingletonObjectDemo singletonObject;

...

if (singletonObject == null) {
        singletonObject = new SingletonObjectDemo();
    }

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

4 голосов
/ 18 июля 2010

Что если мы не будем использовать эту часть кода? Все еще будет единственная копия SingletonObjectDemo, зачем нам тогда этот код?

Идея состоит в том, чтобы загружать синглтон с отложенной загрузкой, то есть загружать экземпляр только тогда, когда это действительно необходимо . Почему вы хотите это сделать? Хорошо, Боб Ли суммирует это довольно хорошо в Ленивые Загрузочные Одиночки :

В производственном процессе вы, как правило, хотите загружать все свои синглтоны, чтобы вы могли своевременно отлавливать ошибки и сразу же оценивать производительность, но в тестах и ​​в процессе разработки вам нужно загружать только то, что вам абсолютно необходимо, чтобы не тратить время ,

Но реализация, которую вы показываете, не работает, она не является поточно-ориентированной, и два параллельных потока могут фактически создать два экземпляра. лучший способ сделать ваш лениво загруженный одноэлементный поток безопасным - это использовать идиому Initialization on Demand Holder (IODH), которая очень проста и имеет zero синхронизации накладные расходы. Цитирую Эффективную Java, Элемент 71: Используйте ленивую инициализацию разумно (ударение не мое):

Если вам нужно использовать отложенную инициализацию для статическое поле, используйте ленивый инициализация класса держателя идиома . Эта идиома (также известный как идиома класса инициализации по требованию ) использует гарантию того, что класс не будет инициализирован до используется [JLS, 12.4.1]. Вот как это выглядит:

// Lazy initialization holder class idiom for static fields
private static class FieldHolder {
    static final FieldType field = computeFieldValue();
}
static FieldType getField() { return FieldHolder.field; }

Когда вызывается метод getField в первый раз, это читает FieldHolder.field за первый время, вызывающее класс FieldHolder инициализироваться. Красота этого идиома в том, что метод getField не синхронизируется и выполняет только доступ к полю, поэтому ленивая инициализация практически ничего не добавляет к стоимости доступа. Современная ВМ будет синхронизировать доступ к полю только с инициализировать класс. Однажды класс инициализируется, виртуальная машина исправит код, чтобы последующий доступ к поле не предполагает каких-либо испытаний или синхронизации.

Смотри также

3 голосов
/ 17 июля 2010

2 строки проверяют, создан ли один-единственный синглтон, и если нет, то он создаст экземпляр синглтона.Если экземпляр уже существует, ничего не делается и возвращается.Экземпляр синглтона создается при первой необходимости по требованию, а не при инициализации приложения.

Обратите внимание, что в вашем коде содержится ошибка race-condition .Когда 2 потока входят одновременно, одноэлементный объект может быть выделен дважды.Это можно исправить, синхронизировав метод следующим образом:

public static synchronized SingletonObjectDemo getSingletonObject() {
    if (singletonObject == null) {
        singletonObject = new SingletonObjectDemo();
    }
    return singletonObject;
}

Кстати, чтобы вернуться к вашему вопросу, строка:

private static SingletonObjectDemo singletonObject;

объявляет статическую ссылку, но не будетфактически выделяя экземпляр, ссылка устанавливается на null компилятором Java.

1 голос
/ 17 июля 2010

Данный класс не создает первый экземпляр объекта до тех пор, пока он не будет запрошен.Поле private static равно null до первого запроса, затем создается экземпляр объекта и сохраняется там.Последующие запросы возвращают тот же объект.

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

0 голосов
/ 17 июля 2010

Сначала singletonObject имеет значение null. Идея синглета состоит в том, чтобы инициализировать этот объект в первый раз, когда кто-то вызывает getSingletonObject (). Если вы не вызовете конструктор в этой части, переменная всегда будет нулевой.

0 голосов
/ 17 июля 2010

Этот код

  • отвечает за создание первого объекта
  • препятствует созданию другого
...