Дан Java-код с паттерном двойной-роверности (double-checked locking):
public Singleton getInstance() { if (instance == null) { synchronized(this) { if (instance == null) instance = new Singleton(); } } return instance; }
Почему это может быть небезопасно в Java без дополнительных мер, и как правильно реализовать ленивую и потокобезопасную инициализацию

28 Авг в 04:12
1 +1
0
Ответы
1
Коротко: исходный код небезопасен потому, что без дополнительных гарантий может произойти переупорядочивание при создании объекта: поток А может выполнить выделение памяти и присвоение ссылки полю `instance` до завершения инициализации объекта; тогда поток Б увидит ненулевую ссылку и получит частично сконструированный объект. Это поведение допускалось моделью памяти Java до и даже без явных гарантий видимости. Правильный способ — гарантировать видимость и порядок через механизмы JMM.
Почему так происходит (кратко):
- Конструктор может выполняться не полностью до того, как ссылка будет записана в `instance` (реализация `new` может быть разбита на шаги: allocate → assign → initialize).
- Без барьеров памяти другой поток может увидеть ненулевую ссылку и работать с неинициализированным объектом.
- Начиная с Java 5+5+5+ модель памяти фиксирована, но требуется использовать соответствующие средства (например, `volatile`) для предотвращения переупорядочивания.
Правильные варианты реализации (коротко, с примерами):
1) Исправление двойной проверки через `volatile` (работает в Java 5+5+5+):
```java
public class Singleton {
private static volatile Singleton instance; // volatile обязателен
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 (лениво, потокобезопасно, без синхронизации при доступе):
```java
public class Singleton {
private Singleton() {}
private static class Holder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
```
3) Enum-синглтон (самый простой и безопасный — сериализация/рефлексия учтены):
```java
public enum Singleton {
INSTANCE;
// методы...
}
```
4) Простой synchronized-метод (корректно, но синхронизация при каждом вызове):
```java
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) instance = new Singleton();
return instance;
}
}
```
Вывод: либо используйте `volatile` вместе с двойной проверкой (при условии Java 5+5+5+), либо предпочтительнее — Holder-idiom или enum для простоты и безопасности.
28 Авг в 04:19
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир