English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Java паттерн дизайна Singleton
Введение:
В процессе разработки программного обеспечения часто встречаются объекты, которые нам нужно использовать только один раз, например: пул потоков (threadpool), кэш (cache), диалоговые окна, предпочтения и т.д. Если создать несколько экземпляров таких объектов, это может привести к нежелательным проблемам, таким как аномальное поведение программы, чрезмерное использование ресурсов и т.д. В таких случаях паттерн Singleton может обеспечить наличие только одного экземпляра класса и предоставить глобальный точку доступа. Низ приведены примеры из простых классов Singleton, чтобы обсудить, какие методы можно использовать для реализации паттерна Singleton.
/** * Самый классический шаблон Singleton */ public class Singleton { // Установлен как статическая переменная для хранения уникального экземпляра Singleton private static Singleton singleInstance; private Singleton(){ // Метод конструктора объявлен как частный, поэтому его можно вызвать только в классе Singleton } /* * Получение объекта Singleton, если объект еще не был создан, то создается новый объект и возвращается этот экземпляр */ public static Singleton getInstance(){ if (singleInstance == null) { singleInstance = new Singleton(); } return singleInstance; } // другие методы }
Из приведенного примера можно看出, что класс Singleton сам управляет процессом создания своего экземпляра и предоставляет глобальный точку доступа, то есть метод getInstance(), который в других классах, использующих Singleton, возвращает экземпляр. Одним из преимуществ этого шаблона Singleton является задержка создания экземпляра, то есть задержка инициализации. Иначе говоря, экземпляр создается только при необходимости, а не при загрузке класса. Это позволяет избежать浪费 производительности. Например, некоторые объекты могут не использоваться в начале выполнения программы или не использоваться вообще в процессе выполнения программы. Однако у этого примера есть и недостаток - он не线程 безопасен. Потому что если несколько потоков одновременно выполняют метод getInstance(), а экземпляр Singleton еще не создан, то все потоки будут думать, что singleInstance равен null, и создадут свои экземпляры Singleton. В результате будет создано несколько экземпляров Singleton, что явно не соответствует初衷 шаблона Singleton. Следующим шагом может быть улучшение этого шаблона
public class SingletonA { private static SingletonA singletongA; private SingletonA(){ } /* * Добавление ключевого слова synchronized для того, чтобы метод getSingletonA стал синхронизированным */ public static synchronized SingletonA getInstanceA(){ if (singletongA == null) { singletongA = new SingletonA(); } return singletongA; } // другие методы }
Из этого примера видно, что добавление synchronized делает getInstanceA() синхронным методом, и thread должен ждать, пока другой thread не покинет этот метод, чтобы войти в него, что позволяет методу выполняться только одним thread в одно время.
Возможно, проблема решена, но следует знать, что синхронизация методов может влиять на эффективность выполнения программы. В этом примере мы просто решаем проблему, чтобы первый раз выполнение getInstance() не создавал несколько экземпляров, а в этом примере каждый раз при необходимости экземпляра вызывается getInstanceA() синхронный метод, а после создания экземпляра вызов synchronized становится избыточным, так как мы уже не должны беспокоиться о создании новых экземпляров singleton класса. Поэтому нам нужно сделать некоторые улучшения.
Поскольку мы говорили о задержке инсталляции, если мы не используем ее, то это будет проще.
public class SingletonB { // создаем singleton в статическом инициализаторе (static initializer), гарантируя thread safety private static SingletonB singletonB = new SingletonB(); private SingletonB(){ // конструктор } public static SingletonB getInstaceB(){ // уже инсталлирован, используйте его напрямую return singletonB; } }
Верхний метод создает экземпляр при загрузке класса JVM, так как JVM создает экземпляр до доступа к нему threads, поэтому это безопасно с точки зрения thread. Но это может привести к浪费у ресурсов по сравнению с задержкой инсталляции. 而且 если такой класс большой, это может увеличить время инициализации программы.
Так можно ли использовать задержку инсталляции, не вызывая Race Condition и не уменьшая эффективность доступа? Давайте улучшим это с помощью двойной проверки блокировки.
/** * Двойная блокировка шаблона singleton */ public class SingletonC { private volatile static SingletonC singletonC; private SingletonC(){ } public static SingletonC getInstanceC(){ if (singletonC == null) { synchronized (SingletonC.class) { if (singletonC == null) { singletonC = new SingletonC(); } } } return singletonC; } }
Пример выше сначала проверяет экземпляр, если его нет, то enters синхронный блок, после того как enters синхронный блок, проверяет снова, если все еще null, то создает экземпляр, поэтому singletonC = new SingletonC() будет выполняться только один раз, а после вызова getInstanceC() сразу возвращается экземпляр, поэтому кроме первого вызова, не будет так, как в втором примере, каждый раз вызывать синхронный метод. Таким образом, можно уменьшить время выполнения getInstanceC(). Наверное, вы заметите, что есть ключевое слово volatile, его функция - после инициализации singletonC он становится видимым для всех потоков, несколько потоков могут правильно обрабатывать эту переменную SingletonC. Но следует отметить: ключевое слово volatile можно использовать только в Java 5 и более поздних версиях, если до этой версии, это может привести к сбою двойной проверки.
При использовании паттерна Singleton, если есть несколько классовых загрузчиков (classloader), необходимо自行 определить классовый загрузчик и определить, что использовать один классовый загрузчик. Porque cada классовый загрузчик определяет пространство имен, разные классовые загрузчики могут загружать одну и ту же класс, что может привести к созданию нескольких экземпляров singleton класса.
Спасибо за чтение, я надеюсь, что это поможет вам, спасибо за поддержку нашего сайта!
Заявление: содержание этой статьи взято из интернета, авторские права принадлежат их авторам. Контент предоставлен пользователями интернета и загружен ими самостоятельно. Этот сайт не owns авторские права, не делает ручную редактуру и не несет ответственности за связанные с этим юридические вопросы. Если вы обнаружите содержание,涉嫌侵犯版权, пожалуйста, отправьте письмо по адресу: notice#oldtoolbag.com (во время отправки письма замените # на @), и предоставьте соответствующие доказательства. При подтверждении侵权事实, этот сайт немедленно удалят涉嫌侵权的内容。