определение
Одноэлементный шаблон является относительно распространенным шаблоном проектирования, цель которого состоит в том, чтобы гарантировать, что класс может иметь только один экземпляр, а также создать экземпляр и предоставить этот экземпляр всей системе, чтобы избежать частого создания объектов и сэкономить память.
Существует множество сценариев применения одноэлементного шаблона.
Например, корзина операционной системы нашего компьютера является хорошим приложением в одноэлементном режиме.После того, как файлы, видео, музыка и т. д. на компьютере будут удалены, они попадут в корзину, и принтер в компьютере также разработан в одноэлементном режиме Да, задач печати в системе может быть несколько, но работающая задача может быть только одна, счетчик веб-страницы также реализован в одноэлементном режиме, поэтому нет необходимости фиксировать каждое обновление в базу данных.
Изучив эти сценарии приложений, мы получили более четкое представление об основной идее одноэлементного паттерна и начнем реализовывать его с помощью кода.
Прежде чем писать код одноэлементного режима, давайте вкратце разберемся в двух важных моментах: о порядке загрузки классов и ключевом слове static.
порядок загрузки классов
Механизм загрузки классов (classLoader) обычно следует следующему порядку загрузки.
Если класс еще не загружен:
- Сначала выполните инициализацию блока статического кода и статической переменной родительского класса Порядок выполнения блоков статического кода и статических переменных зависит от порядка их появления в коде.
- Выполнение статических блоков кода и инициализация статических переменных для подклассов.
- Выполнить инициализацию переменной экземпляра родительского класса
- Выполнить конструктор родительского класса
- Выполнить инициализацию переменной экземпляра подклассов
- Выполнить конструктор подкласса
При этом процесс загрузки класса является приватным для потока, и другие потоки не могут в него войти.
Если класс уже загружен:
Статические блоки кода и статические переменные не выполняются повторно.При создании объекта класса выполняются только методы инициализации и построения переменных, связанных с экземпляром.
статическое ключевое слово
Если переменная-член или метод в классе изменены с помощью ключевого слова static, то переменная-член или метод будут независимы от любого объекта класса. Он не зависит от конкретного экземпляра класса и является общим для всех экземпляров класса. Пока класс загружен, к переменной-члену или методу можно получить доступ через имя класса. Его функция описана в одном предложении:Вызов метода или переменной без создания объекта, который просто создан специально для кодовой реализации шаблона singleton.
Далее будут перечислены несколько реализаций одноэлементного шаблона, все ключевые методы которого украшены статическими элементами, и, чтобы избежать частого создания объектов одноэлементного класса, мы можем использовать частный конструктор, чтобы убедиться, что одноэлементный класс не может быть создан извне.
ленивый и голодный
В программировании одноэлементный режим обычно делится на два типа, а именно стиль голодного человека и стиль ленивого человека.
Голодный китайский стиль: инициализация завершается при загрузке класса, поэтому загрузка класса происходит медленно, но скорость получения объекта высокая.
Ленивый: не инициализируйте класс при его загрузке, подождите, пока он будет использован в первый раз.
Код
1. Голодный китайский стиль (доступно)
public class Singleton {
private final static Singleton INSTANCE = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return INSTANCE;
}
}
Это относительно распространенный способ записи.Создание экземпляра завершается при загрузке класса, что позволяет избежать проблемы синхронизации нескольких потоков. Конечно, есть и недостатки, потому что экземпляр класса создается при его загрузке, что не дает эффекта отложенной загрузки: если экземпляр не используется, память будет потрачена впустую.
2. Обычный ленивый стиль (поток небезопасен, недоступен)
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
Это самый простой способ записи в ленивом стиле, он будет создан только при первом доступе к методу, что обеспечивает эффект ленивой загрузки. Но есть фатальная проблема с этим способом записи, то есть проблема безопасности многопоточности. Предполагая, что экземпляр объекта не был создан, а затем к нему одновременно обращаются два потока, может возникнуть результат множественных инстанций, поэтому этот метод записи нельзя использовать.
3. Ленивый человек синхронных методов (доступен)
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
Это написание правильноеgetInstance()Обработка блокировок добавлена для того, чтобы только один поток мог обращаться и получать экземпляры одновременно, но недостатки тоже очевидны, т.к.synchronizedЭто украшает весь метод, и каждый доступ к потоку должен быть синхронизирован. Фактически, этот метод выполняет код создания экземпляра только один раз. Метод синхронизации явно неэффективен каждый раз. Чтобы улучшить этот метод записи, есть следующий двойной проверить Ленивый парень.
4. Double Check Sloth (доступно, рекомендуется)
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
Этот метод записи использует два суждения if, то есть Double-Check, и синхронизация является не методом, а блоком кода, который более эффективен и является улучшением третьего метода записи. Зачем делать два суждения? Это делается из соображений безопасности потоков, или в этом случае экземпляр объекта еще не создан, а два потока A и B одновременно обращаются к статическому методу и одновременно выполняют первый оператор if. , поток A сначала входит в блок кода синхронизации для создания экземпляра объекта, после окончания поток B также входит в блок кода синхронизации.Если нет второго оператора if Judgement, то поток B также выполнит операцию создания экземпляра объекта.
5. Статические внутренние классы (доступны, рекомендуются)
public class Singleton {
private Singleton() {}
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
Этот метод написания рекомендуется многими разработчиками.Этот статический метод внутреннего класса используется вSingletonКласс создается не сразу при загрузке, а когда его необходимо создать, он вызываетсяgetInstanceметод, загрузитсяSingletonInstanceкласс для завершения создания экземпляра объекта.
В то же время, поскольку статические свойства класса инициализируются только при первой загрузке класса, гарантируется, чтоSingletonInstanceОбъекты создаются только один раз, и этот процесс также является потокобезопасным.
6. Перечень (имеется, рекомендуется)
public enum Singleton {
INSTANCE;
}
Этот способ написания высоко оценен в «Эффективной JAVA», и он может решить две проблемы:
1) Вопросы безопасности потоков. Поскольку виртуальная машина Java использует метод ClassLoader при загрузке классов перечисления, этот метод использует синхронизированные блоки кода для обеспечения безопасности потоков.
2) Избегайте уничтожения объектов десериализацией, потому что десериализация enum не достигается путем отражения.
Что ж, вот несколько способов написания одноэлементного шаблона и, наконец, краткое изложение преимуществ и недостатков одноэлементного шаблона.
Преимущества и недостатки одноэлементного шаблона
преимущество
Существует только один экземпляр класса singleton, что экономит ресурсы памяти.Для некоторых объектов, которые необходимо часто создавать и уничтожать, использование режима singleton может повысить производительность системы;
Одноэлементный режим может устанавливать глобальные точки доступа в системе, оптимизировать и обмениваться данными, например, счетчик страниц упомянутого выше веб-приложения может использовать одноэлементный режим для сохранения значения счетчика.
недостаток
Режим singleton обычно не имеет интерфейса, и в принципе нет другого способа расширить его, кроме модификации кода.