0Pricing
Java Academy · Урок

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

Создавайте потокобезопасный Singleton с помощью блокировки с двойной проверкой, перечисления или шаблона holder.

«Singleton: потокобезопасные реализации» — бесплатный урок Java Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.

Паттерн «Одиночка»

Паттерн «Одиночка» гарантирует, что в JVM существует только один экземпляр класса. Распространённые варианты использования: менеджеры конфигурации, пулы подключений и службы ведения журналов.

Наивная реализация одиночки — не потокобезопасна

Классическая ленивая одиночка даёт сбой при параллельном доступе: два потока могут одновременно пройти проверку на null и создать два экземпляра.

public class Config {
    private static Config instance;
    private Config() {}
    public static Config getInstance() {
        if (instance == null) {              // race condition!
            instance = new Config();
        }
        return instance;
    }
}

Синхронизированный метод — просто, но медленно

Добавление synchronized к getInstance() делает реализацию потокобезопасной, но блокирует каждый вызов, даже после создания экземпляра, создавая ненужные накладные расходы.

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

Блокировка с двойной проверкой

Сначала выполните проверку без блокировки, затем повторите её внутри блока synchronized. Для предотвращения перестановки инструкций поле должно иметь модификатор volatile.

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

Инициализация по требованию с Holder

Самая безопасная и эффективная ленивая одиночка: закрытый статический вложенный класс хранит экземпляр. JVM загружает его только при первом вызове getInstance() — загрузка классов по своей природе потокобезопасна.

public class Config {
    private Config() {}
    private static final class Holder {
        static final Config INSTANCE = new Config();
    }
    public static Config getInstance() {
        return Holder.INSTANCE;
    }
}

Одиночка на основе перечисления

В книге Effective Java рекомендуется перечисление с одной константой. Такой подход лаконичен, потокобезопасен, безопасен при сериализации и защищён от атак через рефлексию.

public enum AppConfig {
    INSTANCE;
    private String apiUrl = "https://api.example.com";
    public String getApiUrl() { return apiUrl; }
}
// Usage:
String url = AppConfig.INSTANCE.getApiUrl();

Инициализация при загрузке

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

public class Config {
    private static final Config INSTANCE = new Config();
    private Config() {}
    public static Config getInstance() { return INSTANCE; }
}

Одиночка и сериализация

Одиночка, сериализованная, а затем десериализованная, создаёт новый экземпляр, если не добавить readResolve(), возвращающий INSTANCE. Подход с перечислением решает эту задачу автоматически.

private Object readResolve() { return INSTANCE; }

Одиночка и атаки через рефлексию

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

private Config() {
    if (Holder.INSTANCE != null) {
        throw new IllegalStateException("Use getInstance()");
    }
}

Одиночка во фреймворках внедрения зависимостей

В Spring компоненты по умолчанию являются одиночками (@Scope("singleton")). Вам редко нужно реализовывать паттерн «Одиночка» вручную — позвольте контейнеру DI управлять им.

@Component  // Spring manages a single instance
public class FeatureFlags {
    private final boolean darkMode = true;
    public boolean isDarkMode() { return darkMode; }
}

Тестирование одиночек

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

Быстрая проверка

Какой подход к реализации одиночки рекомендуется в Effective Java?

Итоги

Для потокобезопасных одиночек используйте инициализацию по требованию с Holder или подход на основе перечисления. Избегайте наивной ленивой инициализации. В приложениях Spring позвольте фреймворку управлять областями видимости.

Часто задаваемые вопросы

Урок «Singleton: потокобезопасные реализации» бесплатный?

Да — полный текст урока «Singleton: потокобезопасные реализации» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.

Чему я научусь в уроке «Singleton: потокобезопасные реализации»?

Создавайте потокобезопасный Singleton с помощью блокировки с двойной проверкой, перечисления или шаблона holder. Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Java Academy?

Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.

Сколько времени занимает урок «Singleton: потокобезопасные реализации»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Java Academy?

Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Singleton: потокобезопасные реализации
  2. Шаблон фабричного метода
  3. Абстрактная фабрика для семейств продуктов
  4. Шаблон Builder с плавным API
← Назад к Java Academy