Разберите проблему с двойной проверкой (double-checked locking) в Java и почему без volatile она может быть небезопасна; приведите безопасную альтернативу

17 Фев в 15:59
17 +1
0
Ответы
1
Коротко и по существу.
Проблема
- Двойная проверка (double-checked locking, DCL) в классическом виде:
```
class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
```
Необходима для уменьшения стоимости синхронизации (синхронизируемся только при инициализации).
- Почему это небезопасно без volatile:
При создании объекта JVM/JIT/процессор могут выполнять операции в другом порядке. Создание объекта по сути может быть представлено как последовательность:
111 выделить память, 222 выполнить конструктор (инициализировать поля), 333 записать ссылку в переменную `instance`.
Релабелинг/перестановка может привести к порядку 111, 333, 222: ссылка записана, но объект ещё не полностью инициализирован. Тогда второй поток увидит `instance != null` и начнёт использовать частично сконструированный объект — это race condition и неисправимое поведение по спецификации Java Memory Model до и без использования специальных средств синхронизации.
- Роль `volatile`:
Объявление `private static volatile Singleton instance;` предотвращает такие перестановки и гарантирует видимость: записи, сделанные в конструкторе до записи `instance`, будут видимы всем последующим чтениям `instance`. В модели памяти Java (с фиксированными семантиками начиная с Java 555) запись в volatile обладает «release» семантикой, чтение — «acquire»; это обеспечивает корректность DCL.
Безопасные альтернативы (рекомендуемые)
1) DCL с volatile (если нужен ленивый init и низкая накладная после инициализации):
```
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;
}
}
```
2) Initialization-on-demand holder (лучше по читаемости и безопасности, лениво и без volatile):
```
class Singleton {
private Singleton() {}
private static class Holder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
```
3) Enum singleton (самый простой и безопасный способ для одиночки):
```
public enum Singleton {
INSTANCE;
// методы
}
```
4) Простая синхронизированная фабрика (правильно, но может быть дороже при частых вызовах):
```
class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) instance = new Singleton();
return instance;
}
}
```
Вывод
- DCL без `volatile` небезопасен из‑за возможной перестановки записи ссылки и инициализации объекта; начиная с Java 555 правильное решение — либо использовать `volatile`, либо предпочтительнее Holder/enum idiom.
17 Фев в 16:05
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир