1. Хип-хоп говорит
Прежде всего, пожалуйста, наслаждайтесьОригинальная песня для одноэлементного режима.
嘻哈说:单例模式
作曲:懒人
作词:懒人
Rapper:懒人
某个类只有一个实例
并自行实例化向整个系统提供这个实例
需要私有构造方法毋庸置疑
自行实例化各有各的依据
提供单一实例则大体一致
饿汉静态变量初始化实例
懒汉初始为空
获取实例为空才创建一次
方法加上锁弄成线程安全的例子
DCL双重检查锁两次判空加锁让并发不是难事
创建对象并不是原子操作因为处理器乱序
volatile的关键字开始用武之地
静态内部类中有一个单例对象的静态的实例
枚举天生单例
容器管理多个单例
Прослушивание, пожалуйста, нажмите здесь
Когда мне нечего делать и слушать музыку, знания наполняют мой разум;
Не следует недооценивать изучение новых способов ношения наушников.
2. Определение
В шаблонах проектирования Java шаблон синглтона относительно прост как шаблон создания.
Что такое созидательный паттерн?
Шаблоны создания — это категория шаблонов проектирования.
Паттерны проектирования можно разделить на три категории: творческие паттерны, структурные паттерны и поведенческие паттерны.
Шаблон создания: предоставляет способ скрыть логику создания при создании объекта вместо использования оператора new для непосредственного создания экземпляра объекта.
Структурный шаблон: сосредоточьтесь на композиции классов и объектов, используйте концепцию наследования для создания интерфейсов и определите, как составные объекты получают новые функции.
Поведенческие паттерны: сосредоточьтесь на общении между объектами.
Давайте посмотрим на определение шаблона singleton.
удостоверитьсяСуществует только один экземпляр класса,исоздать экземпляр самого себя и предоставить этот экземпляр всей системе.
Это,Гарантирует только один экземпляр класса и предоставляет к нему глобальную точку доступа.
В глазах ленивых людей одноэлементный паттерн выглядит так:Обратите внимание Сироты, Отверженные.
3. Особенности
Из определения мы можем проанализировать некоторые характеристики:
Одноэлементный класс может иметь только один экземпляр.
Убедитесь, что существует только один экземпляр класса.
Одноэлементный класс должен сам создать свой собственный уникальный экземпляр..
создать экземпляр самого себя.
Одноэлементный класс должен предоставлять этот экземпляр всем другим объектам.. Предоставьте этот экземпляр всей системе.
Экземпляр синглтона будет долго держаться в памяти, если он не предоставляет доступ ко всем объектам, например, он предоставляет доступ только к классам в пакете, его существование не имеет большого значения.
4. Рутины
Как убедиться, что существует только один экземпляр класса?
Процедура 1: Приватные пустые конструкторы, чтобы избежать множественных экземпляров.
Процедура 2: Самостоятельное создание экземпляра, чтобы убедиться, что в памяти существует только один экземпляр.
Процедура 3: Предоставьте общедоступный статический метод getInstance() и верните один экземпляр.
Программа 1 и Программа 3 являются фиксированными процедурами и в основном не меняются.
Подпрограмма 2 имеет много гибких реализаций, поскольку она гарантированно будет создана только один раз.
Хорошо, тогда я начну кодировать.
5. Код
1. Режим голодного человека
package com.fanqiekt.singleton;
/**
* 饿汉单例模式
*
* @author 番茄课堂-懒人
*/
public class EHanSingleton {
private static EHanSingleton sInstance = new EHanSingleton();
//私有化空构造方法
private EHanSingleton() {}
//静态方法返回单例类对象
public static EHanSingleton getInstance() {
return sInstance;
}
//其他业务方法
public void otherMethods(){
System.out.println("饿汉模式的其他方法");
}
}
Подпрограмма 1: Частный пустой конструктор.
Процедура 2: создайте свой экземпляр, чтобы убедиться, что в памяти существует только одна копия создания.
Метод реализации:Инициализация статических переменных экземпляра.
Принцип реализации: объект singleton инициализируется при загрузке класса и инициализируется только один раз.
Процедура 3: Предоставьте общедоступный статический метод getInstance() и верните один экземпляр.
Почему его называют голодным?
Поскольку голодный человек очень голоден, его нужно инициализировать заранее, чтобы накормить себя.
Резюме из потокобезопасности, преимуществ и недостатков.
Потокобезопасность: использование механизма загрузчика классов, безусловнопотокобезопасностьиз.
Почему ты это сказал?
Метод loadClass классаLoader использует ключевое слово synchronized при загрузке классов.
Преимущества: объект singleton инициализируется при загрузке класса, и первый вызов выполняется быстрее.
Недостаток: одноэлементный объект инициализируется при загрузке класса, что может привести к мусору.
2, ленивый режим
package com.fanqiekt.singleton;
/**
* 懒汉模式
*
* @author 番茄课堂-懒人
*/
public class LazySingleton {
private static LazySingleton sInstance;
//私有化空构造方法
private LazySingleton() {}
//静态方法返回单例类对象
public static LazySingleton getInstance() {
//懒加载
if(sInstance == null) {
sInstance = new LazySingleton();
}
return sInstance;
}
//其他业务方法
public void otherMethods(){
System.out.println("懒汉模式的其他方法");
}
}
Подпрограмма 1: Частный пустой конструктор.
Процедура 2: создайте свой экземпляр, чтобы убедиться, что в памяти существует только одна копия создания.
Метод реализации:Экземпляр пуст в getInstance().
Принцип реализации: если он пустой, создайте экземпляр, если нет, верните экземпляр напрямую.
Процедура 3: Предоставьте общедоступный статический метод getInstance() и верните один экземпляр.
Почему его называют ленивым?
Поскольку ленивые люди ленивы и ленивы в инициализации, они начинают инициализироваться, когда их используют.
Это потокобезопасно?
Это понятно,не потокобезопасныйДа, потому что метод getInstance() не выполняет никакой синхронизации.
что делать?
Заблокировать getInstance().
//静态方法返回单例类对象,加锁
public static synchronized LazySingleton getInstance() {
//懒加载
if(sInstance == null) {
sInstance = new LazySingleton();
}
return sInstance;
}
это становитсяпотокобезопасностьСложный режим исчез.
Каковы плюсы и минусы ленивого режима?
Преимущества: он будет инициализирован только при первом использовании, что экономит ресурсы.
Недостаток: его нужно инициализировать в первый раз, поэтому он будет медленным. После блокировки getInstance() вызовы getInstance() также будут выполняться медленнее.
Есть ли способ снять блокировку getInstance() и при этом сохранить потокобезопасность?
3. ДКЛ
package com.fanqiekt.singleton;
/**
* Double Check Lock 单例
*
* @author 番茄课堂-懒人
*/
public class DCLSingleton {
private static DCLSingleton sInstance;
//私有化空构造方法
private DCLSingleton() {}
//静态方法返回单例类对象
public static DCLSingleton getInstance() {
//两次判空
if(sInstance == null) {
synchronized(DCLSingleton.class) {
if(sInstance == null) {
sInstance = new DCLSingleton();
return sInstance;
}
}
}
return sInstance;
}
//其他业务方法
public void otherMethods(){
System.out.println("DCL模式的其他方法");
}
}
Отличие от ленивого режима:
Снимите блокировку с метода getInstance() и заблокируйте его после того, как экземпляр внутри метода станет пустым.
Преимущество: блокировка будет синхронизирована только тогда, когда экземпляр не инициализирован, что позволяет избежать ситуации блокировки всего метода getInstance().
Полное имя dcl — Double Check Lock,двойная проверкаЗамок. Так называемая двойная проверка является двойной пустой.
Зачем тебе проводить вторую пустоту, это не скидывание штанов и пердеж, это ненужно.
Вы можете подумать, что это просто пук, но на самом деле это неаккуратно, поэтому также необходимо снять штаны.
Бывает такая ситуация, поток 1 и 2 одновременно судят, что первый раз пустой, и блок блокируется в месте добавления блокировки, если нет второго решения, то поток 2 будет выполняться снова после потока 1 выполняется, так что если он инициализируется дважды, возникает проблема.
После двух пустых вызовов DCL гораздо безопаснее, да и вообще проблем не будет. Но когда количество параллелизма особенно велико, риски все равно будут.
Где это находится?
sInstance = новый DCLSingleton() здесь.
Не странно ли, что это очень обычное заявление о создании экземпляра может быть рискованным.
Ситуация такова:
sInstance = new DCLSingleton() не является атомарной операцией, она преобразуется в несколько ассемблерных инструкций, которые примерно делают три вещи:
Шаг 1: Выделите память.
Шаг 2: вызовите конструктор для инициализации.
Шаг 3: Укажите объект sInstanc на выделенное место.
Поскольку компилятор Java позволяет процессору выполняться не по порядку, порядок этих трех шагов не является фиксированным, безусловно, можно выполнять их последовательно, но если выполняются первый и третий шаги, другие потоки сообщат об ошибке, используя сИнстанц.
Как это решить?
Здесь вам нужно использовать ключевое слово volatile.
Какая польза от volatile?
Первое: добиться наглядности.
Что это обозначает?
В текущей модели памяти Java потоки могут хранить переменные в локальной памяти (например, в машинных регистрах) вместо чтения и записи непосредственно в основной памяти.
Это может привести к тому, что один поток изменит значение переменной в основной памяти, в то время как другой поток продолжит использовать свою копию значения переменной в регистре, что приведет к несогласованности данных.
Летучий пригодится в это время.
читать изменчивый: Всякий раз, когда изменчивая переменная используется в операторе дочернего потока, она будет снова скопирована из основного потока, чтобы обеспечить согласованность дочернего потока с основным потоком.
написать изменчивый: Всякий раз, когда оператор подпотока хочет записать изменчивую переменную, он будет синхронизирован с основным потоком после чтения, чтобы обеспечить своевременное обновление переменных основного потока.
Второе: не допустить, чтобы процессор выполнялся не по порядку.
Когда изменчивая переменная инициализирована, она может выполняться только в порядке первого шага, второго шага и третьего шага.
Таким образом, мы можем изменить код объявления переменной sInstance.
private volatile static DCLSingleton sInstance;
Однако, поскольку использование volatile скрывает необходимую оптимизацию кода в JVM, оно относительно неэффективно, поэтому это ключевое слово следует использовать при необходимости.
Это кажется немного сложным для реализации.Есть ли отличный и более простой одноэлементный шаблон?
4. Статический внутренний класс
package com.fanqiekt.singleton;
/**
* 静态内部类单例模式
*
* @author 番茄课堂-懒人
*/
public class StaticSingleton {
//私有静态单例对象
private StaticSingleton() {}
//静态方法返回单例类对象
public static StaticSingleton getInstance() {
return SingleHolder.INSTANCE;
}
//单例类中存在一个静态内部类
private static class SingleHolder {
//静态类中存在静态单例声明与初始化
private static final StaticSingleton INSTANCE = new StaticSingleton();
}
//其他业务方法
public void otherMethods(){
System.out.println("静态内部类的其他方法");
}
}
Подпрограмма 1: Частный пустой конструктор.
Процедура 2: создайте свой экземпляр, чтобы убедиться, что в памяти существует только одна копия создания.
Метод реализации:Объявите статический внутренний класс, в статическом внутреннем классе есть статический экземпляр одноэлементного объекта, getInstance() возвращает статический одноэлементный объект статического внутреннего класса..
Принцип реализации: внутренний класс не будет загружен, когда будет загружен его внешний класс, он будет использоваться только тогда, когда используется внутренний класс. Это позволяет избежать инициализации при загрузке класса, что относится к ленивой загрузке.
Статические переменные в статических внутренних классах инициализируются загрузчиком классов, то есть уникальны в памяти, обеспечивая одноэлементность.
Потокобезопасность: Использование механизма загрузчика классов, Кенпотокобезопасность.
Статические внутренние классы просты, потокобезопасны, загружаются отложенно, поэтомунастоятельно рекомендуется.
Существует еще одна реализация, которую вы, возможно, не представляете, — это перечисление.
5. Перечисление
package com.fanqiekt.singleton;
/**
* 枚举单例模式
*
* @Author: 番茄课堂-懒人
*/
public enum EnumSingleton {
INSTANCE;
//其他业务方法
public void otherMethods(){
System.out.println("枚举模式的其他方法");
}
}
Особенности перечисления:
Гарантированно иметь только один экземпляр.
потокобезопасный.
Бесплатная сериализация.
Можно сказать, что перечисление является естественным синглтоном, и его также можно свободно сериализовать, и после десериализации оно также является синглтоном.
Вышеупомянутые одноэлементные методы будут регенерировать объект после десериализации, что является силой перечисления. Каков принцип нумерации?
Мы можем взглянуть на декомпиляцию сгенерированного перечисления, я вставляю сюда только основную часть.
public final class EnumSingleton extends Enum{
private EnumSingleton(){}
static {
INSTANCE = new EnumSingleton();
}
}
Enum — это обычный класс, наследуемый от класса java.lang.Enum. Таким образом, перечисления имеют всю функциональность классов.
Преимущества его реализации аналогичны преимуществам режима голодного человека.
Кроме того, код делает некоторые другие вещи, такие как: переопределяет метод readResolve и возвращает одиночный элемент, поэтому десериализация также возвращает тот же экземпляр.
6. Контейнер
package com.fanqiekt.singleton;
import java.util.HashMap;
import java.util.Map;
/**
* 容器单例模式
*
* @Author: 番茄课堂-懒人
*/
public class SingletonManager {
private static Map<String, Object> objectMap = new HashMap<>();
//私有化空构造方法
private SingletonManager(){}
//将单例的对象注册到容器中
public static void registerService(String key, Object instance){
if(!objectMap.containsKey(key)){
objectMap.put(key, instance);
}
}
//从容器中获得单例对象
public static Object getService(String key){
return objectMap.get(key);
}
}
Метод реализации:Статическая карта, метод размещения объектов на карте и метод получения объектов на карте..
Принцип реализации: в соответствии с ключом для хранения объекта, если ключ уже существует на карте, он не будет помещен на карту; если ключа нет, он будет помещен на карту, что может гарантировать, что объект каждому ключу соответствует отдельный экземпляр.
Самым большим преимуществом синглтонов-контейнеров является то, что можно управлять несколькими синглтонами.
Этот метод используется в исходном коде Android для получения служб системного уровня через контекст (context.getSystemService(key)).
6. КОНЕЦ
Хотя существует множество способов реализации одноэлементного шаблона, все ониСуществует только один экземпляр класса.
На сегодня это все, в следующий раз будет режим строителя, всем спасибо.