Как Singleton связан с многопоточностью
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
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 — лучший выбор для реальных проектов
- Избегай наивных реализаций в многопоточных приложениях