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

Что такое DoubleCheck проверка?

2.2 Middle🔥 151 комментариев
#Многопоточность и асинхронность

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

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

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

Что такое Double-Checked Locking (DCL)?

Double-Checked Locking (DCL) — это паттерн многопоточного программирования, используемый для ленивой инициализации (lazy initialization) ресурсоёмких объектов в многопоточной среде. Его основная цель — минимизировать накладные расходы на синхронизацию, позволяя избегать блокировки после того, как объект был успешно инициализирован.

Проблема, которую решает DCL

Рассмотрим классическую задачу: создание синглтона (Singleton) в многопоточной среде. Наивная реализация с синхронизацией всего метода получения экземпляра выглядит так:

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

Проблема: каждый вызов getInstance() приводит к блокировке на уровне класса, хотя она критически нужна только при первом создании объекта. Для высоконагруженных приложений это создаёт серьёзные производительности.

Решение через Double-Checked Locking

DCL предлагает сначала проверить условие без блокировки, и только если объект ещё не создан, войти в синхронизированный блок, где выполнить вторую проверку:

public class Singleton {
    private static volatile Singleton instance; // Ключевое слово volatile ОБЯЗАТЕЛЬНО
    
    public static Singleton getInstance() {
        if (instance == null) { // Первая проверка (без блокировки)
            synchronized (Singleton.class) {
                if (instance == null) { // Вторая проверка (под блокировкой)
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Как это работает и почему volatile обязательно?

Без volatile эта конструкция не была бы потокобезопасной в Java до версии 5 (JMM до Java 5). Причина — процесс создания объекта (new Singleton()) не является атомарным с точки зрения процессора и памяти:

  1. Выделение памяти под объект.
  2. Инициализация полей (вызов конструктора).
  3. Присвоение ссылки переменной instance.

JIT-компилятор или процессор могут переупорядочить эти шаги (оптимизация), назначив ссылку instance до полной инициализации объекта. Другой поток, увидев instance != null при первой проверке, может получить частично сконструированный объект.

Ключевое слово volatile (введённое в JMM Java 5) решает эту проблему, обеспечивая:

  • Запрет переупорядочивания операций записи и чтения для этой переменной.
  • Немедленную видимость изменений переменной instance для всех потоков.

Где применяется в Android-разработке?

  1. Ленивая инициализация тяжёлых компонентов: OkHttpClient, Retrofit, RoomDatabase, Gson в синглтонах.
  2. Инициализация провайдеров контекста (Application Context).
  3. Кэширование данных, загружаемых из сети или БД.

Альтернативы DCL в современном Java/Android

  • Инициализация при загрузке класса (Holder idiom) — потокобезопасно и эффективно благодаря механизму загрузки классов JVM:
public class Singleton {
    private Singleton() {}
    
    private static class Holder {
        static final Singleton INSTANCE = new Singleton();
    }
    
    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }
}
  • Использование @Synchronized в Kotlin (но он синхронизирует весь метод).
  • lazy в Kotlin с режимами LazyThreadSafetyMode.SYNCHRONIZED (аналог DCL по умолчанию) или PUBLICATION:
val instance: MyClass by lazy { MyClass() } // По умолчанию SYNCHRONIZED

Вывод

Double-Checked Locking — это важный паттерн оптимизации синхронизации, требующий аккуратного применения с volatile в Java. В Android-разработке его использование оправдано для инициализации ресурсоёмких объектов, хотя в Kotlin предпочтительнее использовать встроенную ленивую инициализацию lazy, которая под капотом реализует аналогичную логику, но более безопасно и идиоматично. Понимание DCL демонстрирует глубокое знание модели памяти Java, проблем многопоточности и принципов оптимизации производительности критически важных секций кода.