Сингтон - паттерн проектирования, который используется, чтобы контролировать число создаваемых объектов, слодовательно он относится семейству паттернов Создатели.
Была тенденция создавать максимум один объект, но в некоторых ситуациях необходимо создать фиксированное число объектов; и именно в этом нам поможет данный паттерн. Обычно, конструктор создается приватным, чтобы убедиться, что внешний мир не может создать объекты и предоставляем один статический метод, который возвращает объект. Создание объекта происходит только если он не был создан до этого.
Со временем люди осознали несколькоп роблем с обычной реализацией синглтона и он был улучшен, чтобы решить эти проблемы. Запомните, что синглтон не хороший или плохой, он просто может подходить для решения вышей проблемы.
В этой статье мы рассмотрим различные реализации синглтонов.
Взглянем на диаграмму класса:
В этой статье мы рассмотрим различные реализации синглтонов.
Взглянем на диаграмму класса:
Применение синглтона
Есть несколько ситуаций, когда целесобразно использовать паттерн синглтон. На пример: логирование, кэширование, балансировка нагрузки, конфигурирование, коммуникации и пулл-коннекшинов в БД. Пример использования синглтона в Java API - класс Runtime.
Но если вам это необходимо, вы можете создать объект в одной JVM и передавать его через сериализацию, в другие JVM (но помните, что вы десириализуете его, так что, все что отмечено static или transient не передается, и где то контракт синглтона может быть нарушен). Вы так же можете использовать RMI объекты, как синглтоны, чтобы удовлетворить вашим потребностям.
Веселого обучения!
Реализация синглтона
Приведем пример простой реализации синглтона. Инициализация произойдет при запуске приожения, так же данный способ является потокобезопасный.public class MySingleton { private static final MySingleton mySingleton = new MySingleton(); private MySingleton(){} public static MySingleton getInstance(){ return mySingleton; } }А вот реализация использующая инициализацию при первом обращении. В многопоточном приложении данная реализация будет плогих решением:
class MySingleton { private static MySingleton mySingleton; private MySingleton(){} public static MySingleton getInstance(){ if(null == mySingleton) { mySingleton = new MySingleton(); } return mySingleton; } }Решение для многопоточной среды должо избежать гонки состояний и убедиться, что не будет нарушения философии синглтона. Но в примере ниже, делать весь метод synchronized - не лучший подход, т.к нам необходимо добавить ограничение только на создание объекта.
class MySingleton { private static MySingleton mySingleton; private MySingleton(){} public synchronized static MySingleton getInstance(){ if(null == mySingleton) { mySingleton = new MySingleton(); } return mySingleton; } }Пример ниже делает все то же, но так же пользуется двойной проверкой, используя блокировку по объекту. Это реализация гарантирует потокобезопасность. Но дополнительный объект, который нужен только для блокировки - не лушчая практика.
class MySingleton { private static MySingleton mySingleton; private static final Object lock = new Object(); private MySingleton(){} public static MySingleton getInstance(){ if(null == mySingleton) { synchronized(lock) { if(null == mySingleton) { mySingleton = new MySingleton(); } } } return mySingleton; } }Другая многопоточная реализация, пользуется двойной проверкой, но с блокировкой на уровне класса. Отметив объект MySingletone, как volatile, мы уверены, что изменения , сделанные в одном потоке, будут видны другим потокам. Данная реализация гарантирует потокобезопасность.
class MySingleton { private volatile static MySingleton mySingleton; private MySingleton() {} public static MySingleton getInstance() { if (null == mySingleton) { synchronized(MySingleton.class) { if (null == mySingleton) { mySingleton = new MySingleton(); } } } return mySingleton; } }Данная реализация предоставляет конструктор, который помешает нарушить контракт синглтона, используя рефлексию.
class MySingleton { private volatile static MySingleton mySingleton; //Reflection может получить доступ к приватному конструктору private MySingleton() throws Exception { if (null == mySingleton) { mySingleton = new MySingleton(); } else { throw new Exception("Это singleton; не ожидается создание более одного экземпляра"); } } public static MySingleton getInstance() throws Exception { if (null == mySingleton) { synchronized(MySingleton.class) { if (null == mySingleton) { mySingleton = new MySingleton(); } } } return mySingleton; } }Очень популярная реализация, с использованием статического класса, которая предоставляет ленивую инициализацию (при первом обращении) и потокобезопасность.
public class MySingleton { private MySingleton() {} private static class SingletonUisngInner { private static MySingleton mySingleton = new MySingleton(); } public static MySingleton getInstance() { return SingletonUisngInner.mySingleton; } }В некоторых обстоятельствах, если ваш синглтон наследуется от Cloneable - вашему классу необходимы дополнительные проверки, чтобы избежать нарушения контракта синглтона. Вам необходимо переопределить методо clone и явно выкинуть ошибку CloneNotSupportedException.
class ClonedClass implements Cloneable { //Некоторая логика public Object clone() throws CloneNotSupportedException { return super.clone(); } } public class MySingleton extends ClonedClass { private MySingleton() {} private static class SingletonUisngInner { private static MySingleton mySingleton = new MySingleton(); } public static MySingleton getInstance() { return SingletonUisngInner.mySingleton; } public Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } }Следующий и наш финальный, очень популярный и умный способ реализации синглтона - использовать Enum, который возьмет все заботы на себя.
public enum EnumSingleton{ INSTANCE; }Иногда, люди говорят о синглтонах в нескольких JVM - разберемся с этим. Синглтон означет, что есть только один объект, и мы хоршо знаем, что жизненным циклом объекта управляет JVM, так что один разделяемый объект, среди нескольких JVM невозможен.
Но если вам это необходимо, вы можете создать объект в одной JVM и передавать его через сериализацию, в другие JVM (но помните, что вы десириализуете его, так что, все что отмечено static или transient не передается, и где то контракт синглтона может быть нарушен). Вы так же можете использовать RMI объекты, как синглтоны, чтобы удовлетворить вашим потребностям.
Веселого обучения!
PS это мой перевод данной статьи