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

Как Singleton связан с многопоточностью

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

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

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

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

Singleton и многопоточность

Проблема потокобезопасности

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

Наивный Singleton (небезопасный)

public class Singleton {
    private static Singleton instance;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();  // RACE CONDITION!
        }
        return instance;
    }
}

Здесь два потока могут пройти проверку if (instance == null) одновременно, оба создадут экземпляры.

Решение 1: Double-Checked Locking

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

Здесь:

  • Первая проверка без блокировки — для оптимизации производительности
  • synchronized блок — для синхронизации потоков
  • Вторая проверка внутри блокировки — на случай, если другой поток создал экземпляр
  • volatile — гарантирует видимость изменений между потоками

Решение 2: Eager Initialization (Ранняя инициализация)

public class Singleton {
    private static final Singleton instance = new Singleton();
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        return instance;
    }
}

Экземпляр создаётся при загрузке класса, что гарантирует потокобезопасность благодаря механизмам загрузки классов JVM.

Решение 3: Bill Pugh Singleton (Lazy Initialization)

public class Singleton {
    private static class SingletonHolder {
        private static final Singleton instance = new Singleton();
    }
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}

Использует inner-class и гарантирует потокобезопасность. Экземпляр создаётся только при первом вызове getInstance().

Решение 4: Enum Singleton (Рекомендуется)

public enum Singleton {
    INSTANCE;
    
    public void doSomething() {
        System.out.println("Doing something");
    }
}

// Использование
Singleton.INSTANCE.doSomething();

Это самый безопасный и рекомендуемый подход:

  • Потокобезопасен по умолчанию
  • Защищён от рефлексии и сериализации
  • Синтаксически чистый

Ключевые моменты

  • volatile предотвращает оптимизацию компилятора
  • synchronized обеспечивает взаимное исключение
  • JVM гарантирует потокобезопасность при загрузке классов
  • Enum — лучший выбор для реальных проектов
  • Избегай наивных реализаций в многопоточных приложениях
Как Singleton связан с многопоточностью | PrepBro