Каков эффективный способ реализации одноэлементного шаблона в Java?

Что такое эффективный способ реализации одноэлементного шаблона в Java?

Ответы

Ответ 1

Используйте перечисление:

public enum Foo {
    INSTANCE;
}

Джошуа Блох объяснил этот подход в своей Эффективной перезагрузке Java в Google I/O 2008: ссылка на видео. Также см. Слайды 30-32 его презентации (effective_java_reloaded.pdf):

Правильный способ реализации Serializable Singleton

public enum Elvis {
    INSTANCE;
    private final String[] favoriteSongs =
        { "Hound Dog", "Heartbreak Hotel" };
    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }
}

Изменить: онлайн-часть "Эффективная Java" сообщает:

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

Ответ 2

В зависимости от использования есть несколько "правильных" ответов.

Так как java5 лучший способ сделать это - использовать перечисление:

public enum Foo {
   INSTANCE;
}

Pre java5, самый простой случай:

public final class Foo {

    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }

    public Object clone() throws CloneNotSupportedException{
        throw new CloneNotSupportedException("Cannot clone instance of this class");
    }
}

Перейдите по коду. Во-первых, вы хотите, чтобы класс был окончательным. В этом случае я использовал ключевое слово final, чтобы пользователи знали, что это окончательно. Затем вам нужно сделать конструктор закрытым, чтобы пользователи не могли создавать свой собственный Foo. Выбрасывание исключения из конструктора не позволяет пользователям использовать отражение для создания второго Foo. Затем вы создаете поле private static final Foo для хранения единственного экземпляра и метод public static Foo getInstance() для его возврата. Спецификация Java гарантирует, что конструктор вызывается только при первом использовании класса.

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

Вы можете использовать private static class для загрузки экземпляра. Тогда код будет выглядеть так:

public final class Foo {

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }
}

Так как строка private static final Foo INSTANCE = new Foo(); выполняется только тогда, когда фактически используется класс FooLoader, это заботится о ленивом экземпляре, и гарантируется, что он будет безопасным потоком.

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

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

Метод readResolve() гарантирует, что будет возвращен единственный экземпляр, даже если объект был сериализован в предыдущем запуске вашей программы.

Ответ 3

Отказ от ответственности: Я только что суммировал все удивительные ответы и написал это в моих словах.


При реализации Singleton у нас есть 2 варианта
1. Ленивая загрузка
2. Ранняя загрузка

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

Самый простой способ реализации Singleton - это

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }
}

Все хорошо, кроме раннего загруженного синглтона. Давайте попробуем загрузить ленивый синглтон

class Foo {

    // Our now_null_but_going_to_be sole hero 
    private static Foo INSTANCE = null;

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT  
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        // Creating only  when required.
        if (INSTANCE == null) {
            INSTANCE = new Foo();
        }
        return INSTANCE;
    }
}

Пока все хорошо, но наш герой не выживет, сражаясь наедине со множеством злых нитей, которые хотят много-много экземпляров нашего героя. Так что давайте защитим его от злого многопоточности

class Foo {

    private static Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        // No more tension of threads
        synchronized (Foo.class) {
            if (INSTANCE == null) {
                INSTANCE = new Foo();
            }
        }
        return INSTANCE;
    }
}

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

class Foo {

    // Pay attention to volatile
    private static volatile Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        if (INSTANCE == null) { // Check 1
            synchronized (Foo.class) {
                if (INSTANCE == null) { // Check 2
                    INSTANCE = new Foo();
                }
            }
        }
        return INSTANCE;
    }
}

Это называется "идиома блокировки с двойной проверкой". Легко забыть изменчивое утверждение и трудно понять, почему это необходимо.
Для деталей: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

Теперь мы уверены в злой нити, но как насчет жестокой сериализации? Мы должны убедиться, что даже при десериализации не создается новый объект

class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static volatile Foo INSTANCE = null;

    // Rest of the things are same as above

    // No more fear of serialization
    @SuppressWarnings("unused")
    private Object readResolve() {
        return INSTANCE;
    }
}

Метод readResolve() гарантирует, что будет возвращен единственный экземпляр, даже если объект был сериализован в предыдущем запуске нашей программы.

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

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    // Wrapped in a inner static class so that loaded only when required
    private static class FooLoader {

        // And no more fear of threads
        private static final Foo INSTANCE = new Foo();
    }

    // TODO add private shouting construcor

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    // Damn you serialization
    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

Да, это наш самый герой :)
Поскольку строка private static final Foo INSTANCE = new Foo(); выполняется только тогда, когда класс FooLoader фактически используется, это заботится о ленивом экземпляре,

и гарантированно ли это потокобезопасность.

И мы зашли так далеко, вот лучший способ достичь всего, что мы сделали, это наилучший из возможных способов

 public enum Foo {
       INSTANCE;
   }

Что внутренне будет рассматриваться как

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();
}

Вот оно! Больше нет страха перед сериализацией, потоками и уродливым кодом. Также ENUMS синглтон лениво инициализируется.

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

-Joshua Блох в "Эффективной Java"

Теперь вы, возможно, поняли, почему ENUMS считаются лучшим способом реализации Singleton, и спасибо за ваше терпение :)
Обновил его в моем блоге.

Ответ 4

Решение, отправленное Stu Thompson, действительно в Java5.0 и более поздних версиях. Но я бы предпочел не использовать его, потому что я думаю, что он подвержен ошибкам.

Легко забыть об изменчивом заявлении и трудно понять, почему это необходимо. Без изменчивости этот код больше не будет потокобезопасным из-за двойного контроля блокировки. Подробнее об этом см. В пункте 16.2.4 Java Concurrency на практике. Вкратце: этот шаблон (до версии Java5.0 или без оператора volatile) может вернуть ссылку на объект Bar, который (еще) находится в неправильном состоянии.

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

class Bar {
    private static class BarHolder {
        public static Bar bar = new Bar();
    }

    public static Bar getBar() {
        return BarHolder.bar;
    }
}

Ответ 5

Защита потолка в Java 5 +:

class Foo {
    private static volatile Bar bar = null;
    public static Bar getBar() {
        if (bar == null) {
            synchronized(Foo.class) {
                if (bar == null)
                    bar = new Bar(); 
            }
        }
        return bar;
    }
}

EDIT. Обратите внимание на модификатор volatile.:) Это важно, потому что без него, другие потоки не гарантируются JMM (модель памяти Java), чтобы увидеть изменения его значения. Синхронизация не позаботится об этом - она ​​только сериализует доступ к этому блоку кода.

EDIT 2: @Bno ответьте на подробности подхода, рекомендованного Биллом Пью (FindBugs), и это может быть лучше. Идите читать и проголосуйте за свой ответ.

Ответ 6

Забудьте ленивую инициализацию, это слишком проблематично. Это самое простое решение:

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

    public static A getInstance() {
        return INSTANCE;
    }
}

Ответ 7

Убедитесь, что он вам действительно нужен. Сделайте google для "singleton anti-pattern", чтобы увидеть некоторые аргументы против него. Похоже, что в этом нет ничего неправильного, но это всего лишь механизм для обнародования некоторых глобальных ресурсов/данных, поэтому убедитесь, что это лучший способ. В частности, я нашел инъекцию зависимостей более полезной, особенно если вы также используете модульные тесты, потому что DI позволяет использовать издеваемые ресурсы для целей тестирования.

Ответ 8

Не забывайте, что Singleton - это только Singleton для загрузчика классов, который загружал его. Если вы используете несколько загрузчиков (контейнеров), у каждого из них должна быть своя версия Singleton.

Ответ 9

Я озадачен некоторыми ответами, которые предлагают DI как альтернативу использованию синглетонов; это не связанные понятия. Вы можете использовать DI для ввода одномодовых или не одиночных (например, для потоков) экземпляров. По крайней мере, это верно, если вы используете Spring 2.x, я не могу говорить для других инфраструктур DI.

Итак, мой ответ на OP был бы (во всех, кроме самого тривиального кода примера):

  • Используйте инфраструктуру DI, например Spring, затем
  • Сделайте это частью вашей конфигурации DI, независимо от того, являются ли ваши зависимости одноточечными, запрашивать область действия, область сеанса или что-то еще.

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

Ответ 10

Подумайте, зачем вам нужен синглтон перед его написанием. Существует квазирелигиозная дискуссия об использовании их, которую вы можете легко споткнуться, если вы используете онлайн-синглы google на Java.

Лично я стараюсь избегать синглетонов как можно чаще по многим причинам, и большинство из них можно найти в сингл-сайтах с поисковой системой. Я чувствую, что довольно часто одиночные игры злоупотребляют, потому что их легко понять всеми, они используются как механизм для получения "глобальных" данных в OO-дизайне, и они используются потому, что легко обойти управление жизненным циклом объекта (или действительно думая о том, как вы можете делать A изнутри B). Посмотрите на такие вещи, как Inversion of Control (IoC) или Injection Dependency (DI) для хорошего среднего уровня.

Если вам действительно нужен один, то википедия имеет хороший пример правильной реализации одноэлементного кода.

Ответ 11

Ниже приведены 3 разных подхода

1) Enum

/**
* Singleton pattern example using Java Enumj
*/
public enum EasySingleton{
    INSTANCE;
}

2) Двойная проверка Блокировка/ленивая загрузка

/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
     private static volatile DoubleCheckedLockingSingleton INSTANCE;

     private DoubleCheckedLockingSingleton(){}

     public static DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

3) Статический метод factory

/**
* Singleton pattern example with static factory method
*/

public class Singleton{
    //initailzed during class loading
    private static final Singleton INSTANCE = new Singleton();

    //to prevent creating another instance of Singleton
    private Singleton(){}

    public static Singleton getSingleton(){
        return INSTANCE;
    }
}

Ответ 12

Я использую Spring Framework для управления моими синглонами. Он не применяет "singleton-ness" класса (который вы не можете делать в любом случае, если задействованы несколько загрузчиков классов), но обеспечивает очень простой способ создания и настройки различных фабрик для создания различных типов объектов.

Ответ 13

Версия 1:

public class MySingleton {
    private static MySingleton instance = null;
    private MySingleton() {}
    public static synchronized MySingleton getInstance() {
        if(instance == null) {
            instance = new MySingleton();
        }
        return instance;
    }
}

Lazy loading, потокобезопасность с блокировкой, низкая производительность из-за synchronized.

Версия 2:

public class MySingleton {
    private MySingleton() {}
    private static class MySingletonHolder {
        public final static MySingleton instance = new MySingleton();
    }
    public static MySingleton getInstance() {
        return MySingletonHolder.instance;
    }
}

Lazy loading, потокобезопасная с неблокирующейся, высокая производительность.

Ответ 14

В Википедии есть несколько примеров одиночных игр, также на Java. Реализация Java 5 выглядит довольно полной и является потокобезопасной (применяется двойная проверка блокировки).

Ответ 15

Если вам не нужна ленивая загрузка, просто попробуйте

public class Singleton {
    private final static Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() { return Singleton.INSTANCE; }

    protected Object clone() {
        throw new CloneNotSupportedException();
    }
}

Если вам нужна ленивая загрузка, и вы хотите, чтобы ваш Singleton был потокобезопасным, попробуйте шаблон двойной проверки

public class Singleton {
        private static Singleton instance = null;

        private Singleton() {}

        public static Singleton getInstance() { 
              if(null == instance) {
                  synchronized(Singleton.class) {
                      if(null == instance) {
                          instance = new Singleton();
                      }
                  }
               }
               return instance;
        }

        protected Object clone() {
            throw new CloneNotSupportedException();
        }
}

Поскольку двойной шаблон проверки не гарантированно работает (из-за некоторой проблемы с компиляторами, я ничего об этом не знаю), вы также можете попытаться синхронизировать весь метод getInstance или создать реестр для всех ваши синглтоны.

Ответ 16

Я бы сказал Enum singleton

Синглтон, использующий enum в Java, обычно является способом объявления enum singleton. Enum singleton может содержать экземплярную переменную и метод экземпляра. Для простоты также обратите внимание, что если вы используете какой-либо метод экземпляра, чем вам необходимо обеспечить безопасность потока этого метода, если он вообще влияет на состояние объекта.

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

/**
* Singleton pattern example using Java Enum
*/
public enum Singleton {
        INSTANCE;
        public void execute (String arg) {
                //perform operation here
        }
}

Вы можете получить к нему доступ Singleton.INSTANCE, гораздо проще, чем вызов метода getInstance() на Singleton.

1.12 Сериализация констант перечисления

Константы континуума сериализуются иначе, чем обычные сериализуемые или внешние объекты. Сериализованная форма константы перечисления состоит исключительно из ее имени; значения поля константы отсутствуют в форме. Чтобы сериализовать константу перечисления, ObjectOutputStream записывает значение, возвращаемое методом имен констант enum. Чтобы десериализовать константу перечисления, ObjectInputStream считывает имя константы из потока; десериализованная константа затем получается путем вызова метода java.lang.Enum.valueOf, передающего константный тип перечисления вместе с полученным именем константы в качестве аргументов. Как и другие сериализуемые или внешние объекты, константы перечисления могут функционировать в качестве целей обратных ссылок, появляющихся впоследствии в потоке сериализации.

Процесс, с помощью которого константы контировки сериализуются, не может быть настроен: любые методы класса writeObject, readObject, readObjectNoData, writeReplace и readResolve, определенные типами перечислений, игнорируются при сериализации и десериализации, Аналогично, любые объявления полей serialPersistentFields или serialVersionUID также игнорируются - все типы перечислений имеют фиксированный serialVersionUID of 0L. Документирование сериализуемых полей и данных для типов перечислений не требуется, поскольку нет изменений в типе отправленных данных.

Цитируется из документации Oracle

Другая проблема с обычными синглтонами заключается в том, что после реализации интерфейса Serializable они больше не остаются Singleton, потому что метод readObject() всегда возвращает новый экземпляр типа конструктора в Java. Этого можно избежать, используя readResolve() и отбрасывая вновь созданный экземпляр, заменив его синглоном, как показано ниже

 // readResolve to prevent another instance of Singleton
 private Object readResolve(){
     return INSTANCE;
 }

Это может стать еще более сложным, если ваш Singleton Class поддерживает состояние, так как вам нужно сделать его переходным, но с Enum Singleton, Serialization гарантируется JVM.


Хорошее чтение

Ответ 17

Может быть, немного опоздали с игрой по этому вопросу, но в реализации синглтона есть много нюансов. Держатель рисунка нельзя использовать во многих ситуациях. И IMO при использовании volatile - вы также должны использовать локальную переменную. Позвольте начать с начала и повторить проблему. Вы поймете, что я имею в виду.


Первая попытка может выглядеть примерно так:

public class MySingleton {

     private static MySingleton INSTANCE;

     public static MySingleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new MySingleton();
        }

        return INSTANCE;
    }
    ...
}

Здесь у нас есть класс MySingleton, который имеет закрытый статический член с именем INSTANCE и открытый статический метод с именем getInstance(). При первом вызове getInstance() член INSTANCE имеет значение null. Затем поток перейдет в состояние создания и создаст новый экземпляр класса MySingleton. Последующие вызовы getInstance() обнаружат, что переменная INSTANCE уже установлена, и, следовательно, не создадут другой экземпляр MySingleton. Это гарантирует, что есть только один экземпляр MySingleton, который используется всеми вызывающими getInstance().

Но у этой реализации есть проблема. У многопоточных приложений будет условие гонки при создании единственного экземпляра. Если несколько потоков выполнения попадают в метод getInstance() одновременно (или около того), каждый из них будет видеть элемент INSTANCE как нулевой. Это приведет к тому, что каждый поток создаст новый экземпляр MySingleton и впоследствии установит элемент INSTANCE.


private static MySingleton INSTANCE;

public static synchronized MySingleton getInstance() {
    if (INSTANCE == null) {
        INSTANCE = new MySingleton();
    }

    return INSTANCE;
}

Здесь мы использовали ключевое слово synchronized в сигнатуре метода для синхронизации метода getInstance(). Это, безусловно, исправит наше состояние гонки. Теперь потоки будут блокироваться и вводить метод по одному. Но это также создает проблему производительности. Эта реализация не только синхронизирует создание отдельного экземпляра, но и все вызовы getInstance(), включая чтение. Чтения не должны быть синхронизированы, поскольку они просто возвращают значение INSTANCE. Поскольку чтение будет составлять основную часть наших вызовов (помните, что создание экземпляров происходит только при первом вызове), мы подвергнемся ненужному снижению производительности при синхронизации всего метода.


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronize(MySingleton.class) {
            INSTANCE = new MySingleton();
        }
    }

    return INSTANCE;
}

Здесь мы перенесли синхронизацию из сигнатуры метода в синхронизированный блок, который оборачивает создание экземпляра MySingleton. Но решает ли это нашу проблему? Что ж, мы больше не блокируем чтения, но мы также сделали шаг назад. Несколько потоков попадут в метод getInstance() в одно и то же время или около того, и все они увидят элемент INSTANCE как нулевой. Затем они попадут в синхронизированный блок, где можно получить блокировку и создать экземпляр. Когда этот поток выходит из блока, другие потоки будут бороться за блокировку, и один за другим каждый поток будет проходить через блок и создавать новый экземпляр нашего класса. Итак, мы вернулись туда, откуда начали.


private static MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

Здесь мы выдаем еще одну проверку изнутри блока. Если элемент INSTANCE уже установлен, пропустите инициализацию. Это называется двойной проверкой блокировки.

Это решает нашу проблему множественной реализации. Но еще раз, наше решение поставило еще одну проблему. Другие темы могут не "видеть", что элемент INSTANCE обновлен. Это из-за того, как Java оптимизирует операции с памятью. Потоки копируют исходные значения переменных из основной памяти в кэш ЦП. Изменения значений затем записываются в этот кэш и считываются из него. Это особенность Java, предназначенная для оптимизации производительности. Но это создает проблему для нашей одноэлементной реализации. Второй поток, обрабатываемый другим процессором или ядром с использованием другого кэша, не увидит изменений, внесенных первым. Это приведет к тому, что второй поток увидит член INSTANCE как ноль, что приведет к созданию нового экземпляра нашего синглтона.


private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    if (INSTANCE == null) {
        synchronized(MySingleton.class) {
            if (INSTANCE == null) {
                INSTANCE = createInstance();
            }
        }
    }

    return INSTANCE;
}

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

Но это простое изменение имеет свою цену. Поскольку мы обходим кэш-память ЦП, мы будем снижать производительность каждый раз, когда работаем с изменчивым членом INSTANCE, что мы делаем 4 раза. Мы дважды проверяем существование (1 и 2), устанавливаем значение (3), а затем возвращаем значение (4). Можно утверждать, что этот путь является крайним случаем, поскольку мы создаем экземпляр только во время первого вызова метода. Возможно, удар по производительности на создание терпимо. Но даже наш основной вариант использования, читает, будет работать на volatile член дважды. Один раз проверить существование, и снова вернуть его значение.


private static volatile MySingleton INSTANCE;

public static MySingleton getInstance() {
    MySingleton result = INSTANCE;
    if (result == null) {
        synchronized(MySingleton.class) {
            result = INSTANCE;
            if (result == null) {
                INSTANCE = result = createInstance();
            }
        }
    }

    return result;
}

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

Я недавно написал статью об этом. Деконструкция Синглтона. Вы можете найти больше информации об этих примерах и пример паттерна "holder". Существует также реальный пример, демонстрирующий изменчивый подход с двойной проверкой. Надеюсь это поможет.

Ответ 18

There are 4 ways to create a singleton in java.

1- eager initialization singleton

    public class Test{
        private static final Test test = new Test();
        private Test(){}
        public static Test getTest(){
            return test;
        }
    }

2- lazy initialization singleton (thread safe)

    public class Test {
         private static volatile Test test;
         private Test(){}
         public static Test getTest() {
            if(test == null) {
                synchronized(Test.class) {
                    if(test == null){test = new Test();
                }
            }
         }

        return test;
    }


3- Bill Pugh Singleton with Holder Pattern (Preferably the best one)

    public class Test {

        private Test(){}

        private static class TestHolder{
            private static final Test test = new Test();
        }

        public static Test getInstance(){
            return TestHolder.test;
        }
    }

4- enum singleton
      public enum MySingleton {
        INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}

Ответ 19

Вот как реализовать простой singleton:

public class Singleton {
    // It must be static and final to prevent later modification
    private static final Singleton INSTANCE = new Singleton();
    /** The constructor must be private to prevent external instantiation */ 
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

Вот как правильно лениво создавать свой singleton:

public class Singleton {
    // The constructor must be private to prevent external instantiation   
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    /** 
     * The static inner class responsible for creating your instance only on demand,
     * because the static fields of a class are only initialized when the class
     * is explicitly called and a class initialization is synchronized such that only 
     * one thread can perform it, this rule is also applicable to inner static class
     * So here INSTANCE will be created only when SingletonHolder.INSTANCE 
     * will be called
     */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

Ответ 20

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

Кроме того, если singleton должен быть seriliazble, все остальные поля должны быть временными, а метод readResolve() должен быть реализован для поддержания инварианта одиночного объекта. В противном случае, каждый раз, когда объект десериализуется, будет создан новый экземпляр объекта. То, что readResolve() делает, заменяет новый объект, считанный readObject(), который заставлял этот новый объект быть собранным мусором, поскольку нет переменной, ссылающейся на него.

public static final INSTANCE == ....
private Object readResolve() {
  return INSTANCE; // original singleton instance.
} 

Ответ 21

Различные способы создания одиночного объекта:

  • В соответствии с Джошуа Блох - Эннум будет лучшим.

  • вы также можете использовать двойную блокировку.

  • Можно использовать даже внутренний статический класс.

Ответ 22

Enum singleton

Простейшим способом реализации Singleton, который является потокобезопасным, является использование Enum

public enum SingletonEnum {
  INSTANCE;
  public void doSomething(){
    System.out.println("This is a singleton");
  }
}

Этот код работает с момента появления Enum в Java 1.5

Двойная проверка блокировки

Если вы хотите закодировать "классический" синглтон, который работает в многопоточной среде (начиная с Java 1.5), вы должны использовать этот.

public class Singleton {

  private static volatile Singleton instance = null;

  private Singleton() {
  }

  public static Singleton getInstance() {
    if (instance == null) {
      synchronized (Singleton.class){
        if (instance == null) {
          instance = new Singleton();
        }
      }
    }
    return instance ;
  }
}

Это не является потокобезопасным до 1,5, поскольку реализация ключевого слова volatile отличается.

Ранняя загрузка Singleton (работает даже до Java 1.5)

Эта реализация создает экземпляр singleton при загрузке класса и обеспечивает безопасность потоков.

public class Singleton {

  private static final Singleton instance = new Singleton();

  private Singleton() {
  }

  public static Singleton getInstance() {
    return instance;
  }

  public void doSomething(){
    System.out.println("This is a singleton");
  }

}

Ответ 23

Для JSE 5.0 и выше применяйте подход Enum, в противном случае используйте статический подход к одноэлементному держателю ((ленивый подход к загрузке, описанный Биллом Пью). Решение Latter также является потокобезопасным, не требуя специальных языковых конструкций (например, летучих или синхронизированных).

Ответ 24

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

public class SingletonImpl {

    private static SingletonImpl instance;

    public static SingletonImpl getInstance() {
        if (instance == null) {
            instance = new SingletonImpl();
        }
        return instance;
    }

    public static void setInstance(SingletonImpl impl) {
        instance = impl;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

Добавленный метод setInstance позволяет установить макетную реализацию одноэлементного класса во время тестирования:

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

Это также работает с ранними подходами инициализации:

public class SingletonImpl {

    private static final SingletonImpl instance = new SingletonImpl();

    private static SingletonImpl alt;

    public static void setInstance(SingletonImpl inst) {
        alt = inst;
    }

    public static SingletonImpl getInstance() {
        if (alt != null) {
            return alt;
        }
        return instance;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

У этого есть недостаток в том, чтобы подвергать эту функциональность и нормальному приложению. Другие разработчики, работающие над этим кодом, могут испытывать соблазн использовать метод setInstance'а для изменения изменения определенной функции и, таким образом, изменения поведения всего приложения, поэтому этот метод должен содержать как минимум хорошее предупреждение в нем javadoc.

Тем не менее, для возможности макета-тестирования (при необходимости), это воздействие кода может быть приемлемой ценой.

Ответ 25

простейший одноэлементный класс

public class Singleton {
  private static Singleton singleInstance = new Singleton();
  private Singleton() {}
  public static Singleton getSingleInstance() {
    return singleInstance;
  }
}

Ответ 26

Я все еще думаю после java 1.5, enum - лучшая доступная реализация singleton, так как она также гарантирует, что даже в многопоточных средах - создается только один экземпляр.

public enum Singleton{ INSTANCE; }

и вы закончили!!!

Ответ 27

Взгляните на это сообщение.

Примеры шаблонов проектирования GoF в основных библиотеках Java

Из лучшего ответа "Singleton",

Синглтон (распознаваемый с помощью методов создания, возвращающих один и тот же экземпляр (обычно сам по себе) каждый раз)

  • java.lang.Runtime # getRuntime()
  • java.awt.Desktop # getDesktop()
  • java.lang.System # getSecurityManager()

Вы также можете узнать пример Singleton из самих классов Java.

Ответ 28

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

  • Он универсальный и многоразовый
  • Он поддерживает ленивую инициализацию
  • Он синхронизируется только до тех пор, пока он не будет инициализирован, тогда поставщик блокировки будет заменен неблокирующим поставщиком.

Увидеть ниже:

public class Singleton<T> implements Supplier<T> {

    private boolean initialized;
    private Supplier<T> singletonSupplier;

    public Singleton(T singletonValue) {
        this.singletonSupplier = () -> singletonValue;
    }

    public Singleton(Supplier<T> supplier) {
        this.singletonSupplier = () -> {
            // The initial supplier is temporary; it will be replaced after initialization
            synchronized (supplier) {
                if (!initialized) {
                    T singletonValue = supplier.get();
                    // Now that the singleton value has been initialized,
                    // replace the blocking supplier with a non-blocking supplier
                    singletonSupplier = () -> singletonValue;
                    initialized = true;
                }
                return singletonSupplier.get();
            }
        };
    }

    @Override
    public T get() {
        return singletonSupplier.get();
    }
}

Ответ 29

Чтобы достичь этого (TRUE Singleton),

  • Убедитесь, что ваш класс final. Другие не могут подклассифицировать его и создать еще один экземпляр
  • Сделайте свой объект singleton как private static final
  • Предоставить метод private constructor и public getInstance().
  • Убедитесь, что этот класс Singleton загружен только одним ClassLoader
  • переопределить readResolve() и вернуть тот же экземпляр.

Полезные ссылки: Все ответы в этом сообщении +

Singleton_pattern: Инициализация по требованию с идиомой из wikipedia

journaldev статья

Демонстрация различных способов достижения Singleton

public final class  Singleton{
    private static final Singleton instance = new Singleton();

    public static Singleton getInstance(){
        return instance; 
    }
    public enum EnumSingleton {
        INSTANCE;   
    }
    private Object readResolve()  {
        return instance;
    }
    public static void main(String args[]){
        System.out.println("Singleton:"+Singleton.getInstance());
        System.out.println("Enum.."+EnumSingleton.INSTANCE);
        System.out.println("Lazy.."+LazySingleton.getInstance());
    }
}
final class  LazySingleton {
    private LazySingleton() {}
    public static LazySingleton getInstance() {
        return LazyHolder.INSTANCE;
    }
    private static class LazyHolder {
        private static final LazySingleton INSTANCE = new LazySingleton();
    }
    private Object readResolve()  {
        return LazyHolder.INSTANCE;
    }
}

выход:

Singleton:[email protected]
Enum..INSTANCE
[email protected]

Ответ 30

Иногда простого " static Foo foo = new Foo();" недостаточно. Просто подумайте о какой-то базовой вставке данных, которую вы хотите сделать.

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

public class Singleton {

    private static Singleton instance = null;

    static {
          instance = new Singleton();
          // do some of your instantiation stuff here
    }

    private Singleton() {
          if(instance!=null) {
                  throw new ErrorYouWant("Singleton double-instantiation, should never happen!");
          }
    }

    public static getSingleton() {
          return instance;
    }

}

Теперь, что происходит? Класс загружается через загрузчик классов. Непосредственно после интерпретации класса из массива байтов VM выполняет блок static {}. что весь секрет: статический блок вызывается только один раз, время, которое данный класс (имя) данного пакета загружается этим одноклассным загрузчиком.