Я уверен, что каждый из читателей знает, что такое шаблон проектирования «Синглтон», но не каждый знает, как его эффективно и правильно программировать.
Данная статья представляет собой попытку объединить существующие знания по данному вопросу.
Кроме того, статью можно рассматривать как продолжение.
замечательное исследование , опубликованный ранее на Хабрахабре.
Неленивый синглтон на Java
Автору известны два способа реализации шаблона с обычной инициализацией.1 статическое поле
+ Простая и прозрачная реализация + Безопасность резьбы - Не ленивая инициализация 2 Перечисление синглтона По мнению Джошуа Блоха, это лучший способ реализовать шаблон [1].public class Singleton { public static final Singleton INSTANCE = new Singleton(); }
public enum Singleton {
INSTANCE;
}
+ Остроумный + Сериализация «из коробки» + Безопасность резьбы прямо из коробки + Возможность использования EnumSet, EnumMap и т. д. + поддержка переключения - Не ленивая инициализация
Ленивый синглтон на Java
На момент написания статьи существует как минимум три допустимых реализации шаблона Singleton с ленивой инициализацией в Java. 1 синхронизированный аксессуар public class Singleton {
private static Singleton instance;
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
+ Ленивая инициализация - Плохая производительность (критическая секция) при наиболее типичном доступе 2 Двойная проверка Блокировка и нестабильность
public class Singleton {
private static volatile Singleton instance;
public static Singleton getInstance() {
Singleton localInstance = instance;
if (localInstance == null) {
synchronized (Singleton.class) {
localInstance = instance;
if (localInstance == null) {
instance = localInstance = new Singleton();
}
}
}
return localInstance;
}
}
+ Ленивая инициализация + Высокая производительность - Поддерживается только в JDK 1.5 [5] 2.1 Почему не работает без волатиля? Проблема с идиомой двойной проверки блокировки заключается в модели памяти Java, а точнее в порядке создания объектов.
Условно этот порядок можно представить следующими этапами [2, 3]: Давайте создадим нового студента: Student s = new Student(), затем 1) local_ptr = malloc(sizeof(Student)) //выделяем память под сам объект; 2) s = local_ptr // инициализация указателя; 3) Студент::ктор(ы); // конструирование объекта (инициализация полей); Таким образом, между вторым и третьим этапами другой поток может получить и начать использовать (при условии, что указатель не равен нулю) не полностью созданный объект. Фактически эта проблема была частично решена в JDK 1.5 [5], но авторы JSR-133 [5] рекомендуют использовать voloatile для блокировки с двойной проверкой.
Причем их отношение к подобным вещам легко увидеть из комментария к спецификации:
Существует ряд распространенных, но сомнительных идиом кодирования, таких как идиома блокировки с двойной проверкой, которые предлагаются, чтобы позволить потокам взаимодействовать без синхронизации.Таким образом, хотя проблема и решена, использование Double Checked Lock без энергозависимой крайне опасно.Почти все такие идиомы недействительны с точки зрения существующей семантики и, как ожидается, останутся недействительны с точки зрения предлагаемой семантики.
В некоторых случаях, в зависимости от реализации JVM, операционной среды, планировщика и т. д., этот подход может не работать.
Однако посредством серии экспериментов, сопровождавшихся просмотром ассемблерного кода, сгенерированного JIT, автору не удалось разрешить такой случай.
Наконец, Double Checked Lock можно использовать без исключения с неизменяемыми объектами (String, Integer, Float и т. д.).
3 Идиома держателя по требованию public class Singleton {
public static class SingletonHolder {
public static final Singleton HOLDER_INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.HOLDER_INSTANCE;
}
}
+ Ленивая инициализация + Высокая производительность - Невозможно использовать для нестатических полей класса.
Производительность
Для сравнения производительности вышеописанных методов использовался микро-бенчмарк [6], определяющий количество элементарных операций (приращений полей) в секунду на объекте Singleton из двух параллельных потоков.Измерения проводились на двухъядерном процессоре Intel Core 2 Duo T7300 2 ГГц, 2 Гб оперативной памяти и клиентской виртуальной машине Java HotSpot™ (сборка 17.0-b17).
Единицей скорости считается количество приращений (и, следовательно, захватов объектов) в секунду * 100 000. (чем больше, тем лучше)
Клиент | Сервер | |
---|---|---|
Синхронизированный метод доступа | 42,6 | 86,3 |
Двойная проверка блокировки и нестабильность | 179,8 | 202,4 |
Держатель по требованию | 181,6 | 202,7 |
Краткое содержание
Можно выделить следующие краткие советы по использованию того или иного подхода для реализации паттерна «Единый» [1].
1) Используйте обычную (не ленивую) инициализацию везде, где это возможно;
2) Для статических полей используйте идентификатор держателя по требованию;
3) Для простых полей используйте Double Chedked Lock и volutable idom;
4) Во всех остальных случаях используйте синхронизированный метод доступа;
Библиотека классов Java и синглтон
Примечательно, что разработчики библиотеки классов Java выбрали самый простой способ реализации паттерна — Syncronized Accessor. С одной стороны, это гарантия совместимости и корректной работы.С другой стороны, это потеря процессорного времени на вход и выход из критической секции при каждом обращении.
Быстрый поиск по grep в исходном коде дал понять, что таких мест в JCL очень много.
Возможно, следующей статьей будет «Что будет, если все классы Singleton правильно написать в библиотеке классов JavaЭ» :) Ссылки [1] Джошуа Блох, Эффективная перезагрузка Java выступление на Google I/O 2008 ( видео ); [2] Блокировка с двойной проверкой и шаблон Singleton ; [3] Декларация «Двойная проверка блокировки нарушена» ; [4] ru.wikipedia.org/wiki/Double-checked_locking [5] JSR-133 [6] Как писать микротесты Теги: #java #шаблоны проектирования #singleton #блокировка с двойной проверкой #java
-
Метод Гарантии Доверия К Блокчейнам
19 Oct, 24