Java singleton instantiation

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

private static final ClassName INSTANCE = new ClassName();

public static ClassName getInstance() {
    return INSTANCE;
}

Пример 2:

private static class SingletonHolder { 
    public static final ClassName INSTANCE = new ClassName();
}

public static ClassName getInstance() {
    return SingletonHolder.INSTANCE;
}

Пример 3:

private static ClassName INSTANCE;

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

    return INSTANCE;
}

В проекте, который я использую ATM, во всем мире используется Пример 2, но я вроде как Пример 3 еще. Существует также версия Enum, но я ее просто не понимаю.

Вопрос здесь - в каких случаях я должен/не должен использовать какие-либо из этих вариантов? Хотя я не ищу длительных объяснений (там много других тем, но все они в конечном итоге превращаются в аргументацию ИМО), я бы хотел, чтобы это было понятно с несколькими словами.

Ответ 1

Самый безопасный и простой способ реализовать singleton в java - это использовать перечисления (как вы упомянули):

public enum ClassName {
    INSTANCE;

    // fields, setters and getters
}

Семантика перечисления гарантирует, что будет только один INSTANCE

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

Ответ 2

Образец 1 не использует ленивую инициализацию.

Образцы 2 и 3 ленивы. В примере 2 используется Инициализация по требованию идиома (IODH), которая имеет отсутствие накладных расходов на синхронизацию. Поэтому он быстрее, чем Sample 3.

В Эффективной Java (пункт 3) Джошуа Блох рекомендует, чтобы одноэлементный тип перечисления - лучший способ реализовать singleton.

Однако, если вы не уверены в типе перечисления, придерживайтесь IODH.

Ответ 3

Прежде всего, убедитесь, что вам нужен синглтон, и что вы хотите предоставить доступ к одноэлементному "глобальному уровню". Я обнаружил, что во многих случаях клиентам синглтона не нужно знать, что это синглтон. Скорее, им просто нужно получить услугу, когда они создаются.

Таким образом, независимо от того, как вы получаете синглтон (если вообще), подумайте об изменении способа доступа ваших классов к этому объекту. Хотя это означает изменение конструкторов и изменение "изменений в распределении", я обнаружил, что рамки внедрения инъекций снижают стоимость. Рамка DI также может затем позаботиться об однотональном экземпляре (например, Guice делает это).

Помимо этого. Вариант 3 является типичной и наиболее распространенной (и потокобезопасной) версией, с которой я знаком. Вариант 1 в основном предназначен для нелазной инициализации (не всегда приемлемой). Я никогда не видел 2 используемых.

Ответ 4

Пример 1: Используйте, если вам не нужен ленивый синглтон.
Образец 2: Никогда не используйте - он вводит в заблуждение слишком много занятий. И внутренний класс просто для хранения одной переменной кажется немного ненужным. Пример 3: Это ленивый Синглтон. Используйте его, если вам это нужно.