Единый магазин для создания одноэлементных паттернов на Java
Синглтон - один из важнейших паттернов проектирования. Определение шаблона singleton согласно wiki:
В программной инженерии одноэлементный шаблон - это шаблон проектирования программного обеспечения, который ограничивает создание экземпляра класса одним «единственным» экземпляром. Это полезно, когда нужен ровно один объект для координации действий в системе.
Это один из наиболее часто задаваемых вопросов на собеседовании, будь то собеседование с интерфейсом или сервером. Если вы разработчик Android и ищете синглтон в kotlin, вот отличная статья:
Здесь мы поговорим об одноэлементном классе исключительно на Java и без сокращений. Мы можем писать одноэлементные классы в разных стилях. мы рассмотрим их плюсы и минусы один за другим.
Статическая инициализация:
Когда мы хотим создать экземпляр одноэлементного объекта еще до того, как класс вызывается в первый раз, мы используем этот тип инициализации.
public class StaticInitializedSingleton { private static final StaticInitializedSingleton instance = new StaticInitializedSingleton(); //private constructor to avoid client applications to use constructor private StaticInitializedSingleton(){} public static StaticInitializedSingleton getInstance(){ return instance; } }
Объект инициализируется во время создания объекта. Это означает, что даже если это не требуется. Итак, у вас есть много одноэлементных классов, использующих много ресурсов, тогда это будет занимать много памяти.
Ленивая инициализация:
public class LazyInitializedSingleton { private static LazyInitializedSingleton instance; private LazyInitializedSingleton() { } public static LazyInitializedSingleton getInstance() { if (instance == null) { instance = new LazyInitializedSingleton(); } return instance; } }
Он создает экземпляр объекта, когда мы используем его в первый раз.
Это может вызвать проблемы, если несколько потоков одновременно пытаются получить доступ к условию if
. Оба потока получат разные экземпляры класса singleton, которые нам не нужны.
Поточно-ориентированный синглтон:
Чтобы сделать блок метода потокобезопасным, мы будем использовать ключевое слово syncronized
.
public class ThreadSafeSingleton { private static ThreadSafeSingleton instance; private ThreadSafeSingleton(){} public static synchronized ThreadSafeSingleton getInstance(){ if(instance == null){ instance = new ThreadSafeSingleton(); } return instance; } }
Эта реализация обеспечивает безопасность потоков, но добавляет дополнительные накладные расходы. Позволь мне объяснить. Если пять потоков пытаются получить доступ к этому методу, то четыре из них будут заблокированы, а один из них будет обращаться к блоку метода. В дополнение к этому мы без необходимости блокируем другие потоки, поскольку экземпляр уже создан. Давайте оптимизируем этот сценарий на следующем этапе.
Потокобезопасный-2 Синглтон:
Мы будем использовать синхронизированный блок только тогда, когда экземпляр равен нулю. так что мы не заблокируем его для любого потока, который получит к нему доступ позже.
public static ThreadSafeSingleton getInstanceUsingDoubleLocking(){ if(instance == null){ synchronized (ThreadSafeSingleton.class) { if(instance == null){ instance = new ThreadSafeSingleton(); } } } return instance; }
Здесь мы обработали блокировку потоков, когда какой-либо поток пытается получить к нему доступ, когда экземпляр уже создан. Но что, если пять потоков пытаются получить к нему доступ впервые. Мы снова блокируем четыре из них, потому что для них instance впервые имеет значение null.
Есть ли способ решить все угловые случаи?
Реализация синглтона Билла Пью:
public class BillPughSingleton { private BillPughSingleton(){} private static class SingletonHelper{ private static final BillPughSingleton INSTANCE = new BillPughSingleton(); } public static BillPughSingleton getInstance(){ return SingletonHelper.INSTANCE; } }
У нас есть частный внутренний статический класс, который содержит экземпляр одноэлементного класса. Когда загружается одноэлементный класс, SingletonHelper
класс не загружается в память, и только когда кто-то вызывает метод getInstance()
, этот класс загружается и создает экземпляр класса Singleton. Вот отличное чтение, объясняющее, почему это потокобезопасно.
Билл Пью Синглтон - лучший подход на данный момент. Это была одна из основных реализаций синглтона. Но его легко разрушить с помощью Java-рефлексии. (это для другой статьи)
Enum Singleton:
Чтобы преодолеть эту ситуацию с Reflection, Джошуа Блох предлагает использовать Enum для реализации шаблона проектирования Singleton, поскольку Java гарантирует, что любое значение enum создается только один раз в программе Java.
public enum EnumSingleton { INSTANCE; public static void doSomething(){ //do something } }
Поскольку значения Java Enum доступны во всем мире, синглтон тоже. Недостатком является то, что тип enum несколько негибкий; например, он не допускает ленивую инициализацию.
Заключение:
Мы обсудили различные подходы к созданию класса Singleton. вы можете выбрать, какой из них выбрать, исходя из ваших требований и ограничений. Я вообще предпочитаю подход theBill Pugh Singleton Implementation
.
Если вам понравилась эта статья, то покажите свою любовь, хлопнув в ладоши.