Что такое шаблон проектирования Singleton (Одиночка)?

Ответ

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

Ключевые характеристики:

  1. Приватный конструктор: Предотвращает создание экземпляра извне с помощью оператора new.
  2. Статическое поле: Содержит единственный экземпляр класса.
  3. Публичный статический метод (например, getInstance()): Возвращает этот экземпляр, создавая его при первом вызове (ленивая инициализация).

Базовый пример (НЕ потокобезопасный):

public class SimpleSingleton {
    private static SimpleSingleton instance; // Статическое поле

    private SimpleSingleton() { // Приватный конструктор
        // Инициализация
    }

    public static SimpleSingleton getInstance() {
        if (instance == null) {
            instance = new SimpleSingleton(); // Ленивая инициализация
        }
        return instance;
    }
}

Потокобезопасные реализации:

1. Double-Checked Locking (для производительности):

public class ThreadSafeSingleton {
    // Ключевое слово volatile гарантирует видимость изменения переменной всеми потоками
    private static volatile ThreadSafeSingleton instance;

    private ThreadSafeSingleton() {}

    public static ThreadSafeSingleton getInstance() {
        if (instance == null) { // Первая проверка (без блокировки)
            synchronized (ThreadSafeSingleton.class) {
                if (instance == null) { // Вторая проверка (под блокировкой)
                    instance = new ThreadSafeSingleton();
                }
            }
        }
        return instance;
    }
}

2. Инициализация при загрузке класса (проще и безопаснее):

public class EagerSingleton {
    // Экземпляр создаётся при загрузке класса JVM
    private static final EagerSingleton INSTANCE = new EagerSingleton();

    private EagerSingleton() {}

    public static EagerSingleton getInstance() {
        return INSTANCE; // Просто возвращаем готовый экземпляр
    }
}

Использование: Классы-менеджеры (конфигурации, подключений к БД), логгеры, кэши, пулы потоков, где важен контроль над ресурсами.

Важно: В многопоточных средах и контейнерах (например, Spring) предпочтительнее использовать dependency injection для управления жизненным циклом «одиночных» объектов.

Ответ 18+ 🔞

А, синглтон, ёпта! Ну это ж классика, блядь, как "Муму" у Тургенева! Один на весь мир, как последний мужик в деревне, которого все соседки доят.

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

Как это работает, блядь?

  1. Конструктор приватный — чтобы всякие умники не лезли с new MyClass(), как козёл на базар. Сам решаю, когда рождаться.
  2. Поле статическое — где этот единственный экземпляр, царь и бог, будет храниться.
  3. Метод getInstance() — глобальная точка доступа, типа "эй, дай-ка сюда того самого мужика!". Если его ещё нет — создаём, лениво, по первому требованию.

Вот тебе наивный, дурацкий вариант (для одного потока):

public class SimpleSingleton {
    private static SimpleSingleton instance;

    private SimpleSingleton() {
        // Тут можешь инициализировать своё королевство
    }

    public static SimpleSingleton getInstance() {
        if (instance == null) {
            instance = new SimpleSingleton(); // Рождается по заявке
        }
        return instance;
    }
}

Но если с двух потоков одновременно прилетит getInstance() — может родиться два близнеца, а это уже не синглтон, а пиздец. Два логгера, два коннекта к базе — всё, анархия!

Поэтому для взрослых, многопоточных проектов:

1. Double-Checked Locking (Хитрый, как жопа с ручками)

public class ThreadSafeSingleton {
    private static volatile ThreadSafeSingleton instance; // volatile — чтобы не накосячила оптимизация

    private ThreadSafeSingleton() {}

    public static ThreadSafeSingleton getInstance() {
        if (instance == null) { // Первый быстрый чек, без блокировки
            synchronized (ThreadSafeSingleton.class) { // Теперь серьёзно, под замок
                if (instance == null) { // Второй чек, уже под прицелом
                    instance = new ThreadSafeSingleton();
                }
            }
        }
        return instance;
    }
}

2. Инициализация при загрузке класса (Простой и надёжный, как кирпич)

public class EagerSingleton {
    // JVM создаст этот экземпляр сразу, при загрузке класса. Ленивости ноль, зато безопасно.
    private static final EagerSingleton INSTANCE = new EagerSingleton();

    private EagerSingleton() {}

    public static EagerSingleton getInstance() {
        return INSTANCE; // Просто выдаём то, что уже есть
    }
}

Где это, блядь, применять? Да везде, где нужен один-единственный главный по тарелочкам: менеджер конфигурации, логгер (чтобы все в одну трубу писали), соединение с базой данных, кэш, пул потоков. Главное — не злоупотреблять, а то превратишь всё в одно большое глобальное говно, в рот меня чих-пых!

И да, в современных фреймворках вроде Spring эту хуйню часто берут на себя — они сами создают бины в единственном экземпляре. Но понимать, как это работает изнутри — must have, иначе будешь как Герасим, только "Муму" говорить.