1. Что такое синглтонский режим
Синглтон -шаблон относится к существованию только одного экземпляра на протяжении всей жизни применения. Синглтон -паттерн - широко используемый дизайн. У него есть много преимуществ, которые могут избежать дублирования создания объектов экземпляров, уменьшить накладные расходы системы создания экземпляров и сохранить память.
Существует три требования к режиму синглтона:
2. разница между синглтонским рисунком и статическим классом
Во -первых, давайте поймем, что такое статический класс. Статический класс означает, что класс имеет статические методы и статические поля. Конструктор модифицируется частным, поэтому он не может быть создан. Математический класс - это статический класс.
Узнав, что такое статический класс, давайте поговорим о разнице между ними:
1) Прежде всего, шаблон Singleton предоставит вам глобально уникальный объект. Статический класс предоставляет вам только много статических методов. Эти методы не нужно создавать и могут быть вызваны непосредственно через класс;
2) Одиночный рисунок обладает более высокой гибкостью, а методы могут быть переопределены, потому что все статические классы - все статические методы, поэтому их нельзя переопределить;
3) Если это очень тяжелый объект, односпальный рисунок может быть лень загружать, но статические классы не могут этого сделать;
Затем следует использовать статические классы, а когда мы должны использовать синглтонский режим? Прежде всего, если вы просто хотите использовать некоторые методы инструмента, лучше всего использовать статические классы. Статические аналогии быстрее, чем классы синглтона, потому что статическое связывание выполняется в течение периода компиляции. Если вы хотите сохранить информацию о состоянии или ресурсы доступа, вам следует использовать режим Singleton. Можно также сказать, что, когда вам нужны объектно-ориентированные возможности (такие как наследование, полиморфизм), выбирайте классы Синглтона, а когда вы предоставляете только некоторые методы, выбирайте статические классы.
3. Как реализовать режим синглтона
1. Режим голодного человека
Так называемый голодный режим должен загружать немедленно. Как правило, экземпляры были сгенерированы перед вызовом метода GetInstancef, что означает, что он был сгенерирован при загрузке класса. Недостаток этой модели очень очевиден, что заключается в том, что она занимает ресурсы. Когда класс Singleton большой, мы действительно хотим его использовать, а затем генерировать экземпляры. Следовательно, этот метод подходит для классов, которые занимают меньше ресурсов и будут использоваться во время инициализации.
Class Singletonhungary {Private Static Singletonhungary Singletonhungary = new Singletonhungary (); // Установите конструктор на частное, чтобы запретить экземпляры через новый частный Singletonhungary () {} public static singletonhungary getInstance () {return singletonhungary; }}2. Ленивый режим
Ленивый режим - это ленивая загрузка, также называемая ленивой загрузкой. Создайте экземпляр, когда необходимо использовать программу, чтобы память не была потрачена впустую. Для ленивого режима есть 5 методов реализации. Некоторые методы реализации-это невыполнение потока, что означает, что проблемы синхронизации ресурсов могут возникать в многопоточной среде параллелизма.
Прежде всего, первый метод заключается в том, что в отдельной потоке нет проблем, но в мультипотчике возникнут проблемы.
// ленивая реализация синглтонского режима 1-потока небезопасного класса singletonlazy1 {private static singletonlazy1 singletonlazy; private singletonlazy1 () {} public static singletonlazy1 getInstance () {if (null == singletonlazy) {try {// Совместно некоторую подготовку перед созданием объекта. } catch (прерванное искусство e) {e.printstacktrace (); } singletonlazy = new singletonlazy1 (); } вернуть Singletonlazy; }}Давайте имитируем 10 асинхронных потоков для проверки:
открытый класс singletonlazytest {public static void main (string [] args) {thread2 [] threadArr = new Thread2 [10]; for (int i = 0; i <thenkarr.length; i ++) {threadArr [i] = new Thread2 (); Thinkarr [i] .start (); }}} // Тестовая потока Class Thread2 Extends Thread {@Override public void run () {system.out.println (singletonlazy1.getinstance (). Hashcode ()); }}Результаты работы:
124191239
124191239
872096466
1603289047
1698032342
1913667618
371739364
124191239
1723650563
367137303
Вы можете видеть, что их хэшкоды не совсем одинаковы, что означает, что несколько объектов генерируются в многопоточной среде, которая не соответствует требованиям синглтона.
Так как сделать нить безопасной? Во втором методе мы используем синхронизированное ключевое слово для синхронизации метода GetInstance.
// Синглтон режим ленивый реализация 2-безопасность // Постановление метода синхронизации, эффективность слишком низкая, весь метод-это заблокированный класс singletonlazy2 {private static singletonlazy2 singletonlazy; private singletonlazy2 () {} public static synchronized singletonlazy2 getInstance () {try {if (null == singletonlazy) {// Симуляция для некоторой подготовки перед созданием объекта Thread.sleep (1000); singletonlazy = new singletonlazy2 (); }} catch (прерывание Exception e) {e.printStackTrace (); } вернуть Singletonlazy; }}Используя приведенный выше тестовый класс, результаты теста:
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
Как видно, этот метод обеспечивает безопасность потока. Тем не менее, недостаток в том, что эффективность слишком низкая и синхронно работает. Если следующий поток хочет получить объект, он должен ждать, пока предыдущий поток выпустит, прежде чем он сможет продолжить выполнение.
Затем мы не можем заблокировать метод, но заблокировать код внутри, который также может достичь безопасности потока. Но этот метод такой же, как и метод синхронизации, и он также работает синхронно и имеет очень низкую эффективность.
// Реализация Singletonlazy 3-Стежная безопасность // Настройка синхронных кодовых блоков, эффективность слишком низкая, а весь кодовый блок-это заблокированный класс Singletonlazy3 {Private Static Singletonlazy3 Singletonlazy; private singletonlazy3 () {} public static singletonlazy3 getInstance () {try {synchronized (singletonlazy3.class) {if (null == singletonlazy) {// моделирование, чтобы сделать некоторую подготовку перед созданием объекта. Sleep (1000); singletonlazy = new singletonlazy3 (); }}} catch (прерывание Exception e) {// todo: обработка exception} return singletonlazy; }}Давайте продолжим оптимизировать код. Мы блокируем только код, который создает объект, но может ли это обеспечить безопасность потока?
// Ленивая реализация Синглтонского режима 4-Стежная небезопасная // Только код, который создает экземпляры, синхронизируется путем установки блоков кода синхронизации //, но все еще есть проблемы с безопасностью потоков класс singletonlazy4 {private static singletonlazy4 singletonlazy; private singletonlazy4 () {} public static singletonlazy4 getInstance () {try {if (null == singletonlazy) {// code 1 // моделирование для некоторой подготовки перед созданием Theck Tread.sleep (1000); синхронизированный (singletonlazy4.class) {singletonlazy = new singletonlazy4 (); // code 2}}} catch (прерывание Exception e) {// todo: обработка exception} return singletonlazy; }}Давайте посмотрим на результаты бега:
1210004989
1425839054
1723650563
389001266
1356914048
389001266
1560241484
278778395
124191239
367137303
Судя по результатам, этот метод не может гарантировать безопасность потока. Почему? Давайте предположим, что два потока A и B переходят в «Код 1» одновременно, потому что в настоящее время объект все еще пуст, поэтому оба могут ввести метод. Наденьте сначала захватывает блокировку и создает объект. После того, как поток B получит блокировку, он также перейдет в «Код 2», когда он будет выпущен, и создается объект. Следовательно, синглтон не может быть гарантирован в многопоточной среде.
Давайте продолжим оптимизировать. Поскольку существует проблема с приведенным выше методом, мы можем просто сделать нулевое суждение в блоке кода синхронизации. Этот метод является нашим механизмом двойной блокировки DCL.
// Slazy Man in Singleton Mode реализует 5-резкую безопасность // путем установки блока кода синхронизации, используйте механизм двойной блокировки DCL // Использование механизма двойной проверки блокировки. Когда создается объект SingletonLazy5, когда получается объект SingletonLazy5, нет необходимости проверять блокировку кода синхронизации и последующего кода и непосредственно возвращать объект Singletonlazy5 // функция второго, если решение: решить проблемы безопасности в многопользовании, то есть для обеспечения уникальности объекта. Class SingletonLazy5 {Private Static Statial Singletonlazy5 Singletonlazy; private singletonlazy5 () {} public static singletonlazy5 getInstance () {try {if (null == singletonlazy) {// Совместно некоторую подготовку перед созданием объекта. синхронизированный (singletonlazy5.class) {if (null == singletonlazy) {singletonlazy = new singletonlazy5 (); }}}} catch (прерывание Exception e) {} return singletonlazy; }}Результаты работы:
124191239
124191239
124191239
124191239
124191239
124191239
124191239
124191239
124191239
124191239
Мы видим, что механизм двойной проверки DCL решает эффективность и проблемы безопасности потока ленивого режима однолетнего однолетного. Это также метод, который мы используем чаще всего.
нестабильное ключевое слово
Здесь я заметил, что при определении Singletonlazy используется нестабильное ключевое слово. Это должно предотвратить переупорядочение инструкций. Зачем нам это делать? Давайте посмотрим на сценарий:
Код переходит в singletonlazy = new singletonlazy5 (); Похоже, это предложение, но это не атомная операция (либо все выполнены, либо все не выполнены, и половина не может быть выполнена). Это предложение составлено в 8 инструкций по сборке, и примерно 3 вещи сделаны:
1. Выделите память на экземпляр Singletonlazy5.
2. Инициализировать конструктор Singletonlazy5
3. Укажите объект Singletonlazy на выделенное пространство памяти (обратите внимание, что этот экземпляр не является нулевым).
Поскольку компилятор Java позволяет процессору выполнять вне порядка (Out Order) и порядок кеша, регистры на основную записи памяти в JMM (Java Memory Medel) до JDK1.5, порядок второго и третьего выше не может быть гарантирован. То есть порядок выполнения может быть 1-2-3 или 1-3-2. Если это последнее, и до того, как 3 будет выполнено, и 2 не выполняется, он переключается на потоку 2. В настоящее время Singletonlazy уже выполнил третий пункт в одном потоке, Singletonlazy уже не пусто, поэтому поток два непосредственно принимает Singletonlazy, затем использует ее, а затем естественно сообщает об ошибке. Более того, такая ошибка, которую трудно отслеживать и трудно воспроизвести, не может быть найдена в последнюю неделю отладки.
Метод написания DCL для реализации Singletons рекомендуется во многих технических книгах и учебниках (включая книги, основанные на предыдущих версиях JDK1.4), но на самом деле это не совсем правильно. Действительно, DCL возможен на некоторых языках (таких как C), в зависимости от того, может ли порядок 2 и 3 шагов быть гарантированным. После JDK1.5 чиновник заметил эту проблему, поэтому JMM отрегулирован, и летучие ключевое слово было конкретировано. Следовательно, если JDK является версией 1,5 или более поздней версии, вам нужно только добавить нестабильное ключевое слово в определение Singletonlazy, что может гарантировать, что Singletonlazy читается из основной памяти каждый раз, и переупорядочение может быть запрещено, и вы можете использовать метод написания DCL для завершения режима Singleton. Конечно, нестабильный будет более или менее влиять на производительность. Самое главное, что нам также необходимо рассмотреть JDK1.42 и предыдущие версии, поэтому улучшение написания синглтона по -прежнему продолжается.
3. Статический внутренний класс
Основываясь на приведенных выше соображениях, мы можем использовать статические внутренние классы для реализации шаблона Singleton, код выглядит следующим образом:
// реализовать режим Singleton со статическими внутренними классами-класс безопасности Singletonstaticinner {private singletonstaticinner () {} private static class singletoninner {private static singletonstaticinner singletonstaticinner = new singletonstaticinner (); } public static singletonstaticinner getInstance () {try {thread.sleep (1000); } catch (прерванная экспрессия e) {// todo автоматически сгенерированный блок e.printstacktrace (); } вернуть singletoninner.singletonstaticinner; }}Видно, что мы не выполняем какие -либо операции синхронизации таким образом, так как это обеспечивает безопасность потока? Как и режим «Голодного человека», это функция, которую JVM гарантирует, что статические члены класса могут быть загружены только один раз, так что с уровня JVM имеется только один объект экземпляра. Итак, вопрос в том, в чем разница между этим методом и моделью голодного человека? Разве это не загружается сразу? На самом деле, когда класс загружен, его внутренний класс не будет загружаться одновременно. Загружается класс, который возникает, когда и только при том, что только один из его статических элементов (статические домены, конструкторы, статические методы и т. Д.).
Можно сказать, что этот метод является оптимальным решением для реализации синглтонского шаблона.
4. Статические кодовые блоки
Вот статический шаблон реализации блока статического кода. Этот метод похож на первый, и это также модель голодного человека.
// Использование статических кодовых блоков для реализации односменного режима класса Singletonstaticblock {Private Static Singletonstaticblock singletonstaticblock; static {singletonstaticblock = new singletonstaticblock (); } public static singletonstaticblock getInstance () {return singletonstaticblock; }}5. сериализация и десериализация
Почему LZ рекомендует сериализацию и десериализацию? Потому что, хотя режим Singleton может обеспечить безопасность потока, в случае сериализации и десериализации будут генерироваться несколько объектов. Запустите следующий тестовый класс,
открытый класс singletonstaticinnerserializetest {public static void main (string [] args) {try {singletonstaticinnerialize serialize = singletonstaticinnerialize.getinstance (); System.out.println (serialize.hashcode ()); // serialize fileOutputStream fo = new FileOutputStream ("TEM"); ObjectOutputStream OO = new ObjectOutputStream (fo); OO.WriteObject (serialize); oo.close (); fo.close (); // deserialize fileInputStream fi = new FileInputStream ("TEM"); ObjectInputStream oi = new ObjectInputStream (fi); Singletonstaticinnerialize serialize2 = (singletonstaticinnerialize) oi.readobject (); oi.close (); fi.close (); System.out.println (serialize2.hashcode ()); } catch (Exception e) {e.printstackTrace (); }}} // Использовать анонимные внутренние классы для реализации шаблона Singleton. При столкновении с сериализацией и десериализацией тот же экземпляр не получен .//sole Эта проблема состоит в том, чтобы использовать метод ReadResolve во время сериализации, то есть удалить часть комментариев. Class Singletonstaticinnerserialize реализует Serializable { / *** 28 марта 2018 г.* / Private Static Long Long Serialversionuid = 1L; Частный статический класс innerclass {private static singletonstaticinnerialize singletonstaticinnerserialize = new Singletonstaticinnerialize (); } public static singletonstaticinnerialize getInstance () {return innerClass.singletonstaticinnerialize; } // Защищенный объект readresolve () {// System.out.println («Метод ReadResolve был назван"); // return innerClass.singletonstaticinnerialize; //}}Вы можете увидеть:
865113938
1078694789
Результаты показывают, что это действительно два разных экземпляра объекта, которые нарушают шаблон Синглтона. Так как решить эту проблему? Решение состоит в том, чтобы использовать метод readresolve () в десериализации, удалить вышеуказанный код комментария и запустить его снова:
865113938
Метод чтения был вызван
865113938
Вопрос в том, кто является священным способом метода ReadResolve ()? Фактически, когда JVM десериализуется и «собирает» новый объект из памяти, он автоматически вызовет этот метод ReadResolve, чтобы вернуть указанный нами объект, и гарантированы правила Синглтона. Появление readresolve () позволяет программистам контролировать объекты, полученные посредством десериализации самостоятельно.
Выше всего содержание этой статьи. Я надеюсь, что это будет полезно для каждого обучения, и я надеюсь, что все будут поддерживать Wulin.com больше.