В чем преимущество Lazy Singleton перед Eager Singleton?

«В чем преимущество Lazy Singleton перед Eager Singleton?» — вопрос из категории Паттерны, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Lazy Singleton (Ленивая инициализация) — это вариант реализации паттерна Одиночка, при котором единственный экземпляр класса создается не при загрузке класса, а в момент первого вызова метода получения экземпляра (например, getInstance()).

Ключевое преимущество перед Eager Singleton:

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

Пример потокобезопасной реализации на Java с использованием synchronized:

public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {
        // Приватный конструктор
    }

    // Синхронизированный метод для потокобезопасности
    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

Недостаток и улучшение:

  • Производительность: Синхронизация всего метода getInstance() создает накладные расходы при каждом вызове.
  • Решение — Double-Checked Locking:

    public class LazySingletonDCL {
    // volatile гарантирует видимость изменения instance всеми потоками
    private static volatile LazySingletonDCL instance;
    
    private LazySingletonDCL() {}
    
    public static LazySingletonDCL getInstance() {
        if (instance == null) { // Первая проверка (без блокировки)
            synchronized (LazySingletonDCL.class) {
                if (instance == null) { // Вторая проверка (под блокировкой)
                    instance = new LazySingletonDCL();
                }
            }
        }
        return instance;
    }
    }

    Альтернатива (рекомендуемая): Использование статического внутреннего класса (Initialization-on-demand holder idiom), которая является потокобезопасной и эффективной.

    public class BillPughSingleton {
    private BillPughSingleton() {}
    
    private static class SingletonHolder {
        private static final BillPughSingleton INSTANCE = new BillPughSingleton();
    }
    
    public static BillPughSingleton getInstance() {
        return SingletonHolder.INSTANCE; // Инициализация происходит здесь
    }
    }