← Назад к вопросам

Для чего нужен Lazy Singleton?

1.8 Middle🔥 161 комментариев
#SOLID и паттерны проектирования

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Lazy Singleton в Java

Концепция

Lazy Singleton — это паттерн, при котором единственный экземпляр объекта создаётся только при первом обращении к нему, а не при инициализации программы. Это оптимизация памяти и времени запуска приложения.

Зачем это нужно

1. Экономия памяти

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

2. Ускорение запуска приложения

Время запуска не будет замедляться инициализацией неиспользуемых объектов.

3. Отложенная инициализация

Объект инициализируется только когда это действительно нужно.

Реализация

Вариант 1: Простой Singleton (Eager Initialization)

public class Database {
    private static final Database INSTANCE = new Database(); // Создаётся сразу
    
    private Database() {}
    
    public static Database getInstance() {
        return INSTANCE;
    }
}

Проблема: объект создаётся при загрузке класса, даже если его никогда не используют.

Вариант 2: Lazy Singleton с синхронизацией

public class Database {
    private static Database instance;
    
    private Database() {}
    
    public static synchronized Database getInstance() {
        if (instance == null) {
            instance = new Database(); // Создаётся при первом обращении
        }
        return instance;
    }
}

Плюсы: объект создаётся лишь при необходимости.

Минусы: synchronized замедляет каждый вызов getInstance() из-за блокировки.

Вариант 3: Double-Checked Locking (DCL)

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

Плюсы:

  • После инициализации блокировка не используется
  • Быстро после первого создания

Минусы:

  • Сложный для понимания
  • Требует volatile
  • Может быть проблемным на некоторых платформах

Вариант 4: Bill Pugh Singleton (Самый надежный)

public class Database {
    private Database() {}
    
    private static class Holder {
        static final Database INSTANCE = new Database();
    }
    
    public static Database getInstance() {
        return Holder.INSTANCE; // Создаётся при первом вызове
    }
}

Плюсы:

  • Использует classloader для синхронизации (безопасно)
  • Объект создаётся лишь при первом обращении
  • Нет явной синхронизации
  • Быстрое выполнение
  • Работает даже при отражении

Это рекомендуемый подход!

Вариант 5: Enum Singleton (Самый простой и безопасный)

public enum Database {
    INSTANCE;
    
    private String config;
    
    Database() {
        this.config = "initialized";
    }
    
    public void query(String sql) {
        System.out.println("Executing: " + sql);
    }
}

// Использование
public class Main {
    public static void main(String[] args) {
        Database.INSTANCE.query("SELECT * FROM users");
    }
}

Плюсы:

  • Максимально простой
  • Автоматическая сериализация
  • Защита от отражения
  • Построен на уровне языка

Сравнение подходов

ПодходLazyThread-safeПростотаРекомендация
Eager✓✓✓Если используется всегда
Synchronized✓✓Редко
DCLНе рекомендуется
Bill Pugh✓✓Отличный выбор
Enum✓✓✓Лучший выбор

Практический пример

public class ConfigManager {
    private static class Holder {
        static final ConfigManager INSTANCE = new ConfigManager();
    }
    
    private Map<String, String> config;
    
    private ConfigManager() {
        // Дорогостоящая инициализация
        this.config = loadConfigFromDatabase();
    }
    
    public static ConfigManager getInstance() {
        return Holder.INSTANCE;
    }
    
    public String getConfig(String key) {
        return config.get(key);
    }
    
    private Map<String, String> loadConfigFromDatabase() {
        // Имитация дорогой операции
        return new HashMap<>();
    }
}

Вывод

Lazy Singleton используется для отложенного создания дорогостоящих объектов. Лучший современный подход — Enum (простой и безопасный) или Bill Pugh (для обычных классов). Избегайте Double-Checked Locking в новом коде — это чревато ошибками.

Для чего нужен Lazy Singleton? | PrepBro