Заключение онлайн:
Давайте сначала посмотрим на общие выводы в Интернете:
Разница между так называемым «стилем ленивого человека» и «стилем голодного человека» заключается во времени создания объекта-одиночки.
«Ленивый» — это создание одноэлементного объекта только тогда, когда вы его используете.
«Голодный стиль заключается в создании статического объекта одновременно с созданием класса. Неважно, если вы не можете его использовать, создайте этот одноэлементный объект с самого начала.
Не вдаваясь в заключение, посмотрите на следующее
Код:
голодный китаец
public class Singleton1 {
private final static Singleton1 singleton = new Singleton();
private Singleton1() {
System.out.println("饿汉式单例初始化!");
}
public static Singleton1 getSingleton () {
return singleton;
}
}
Непосредственно новый синглтон в статической переменной класса
ленивый
public class Singleton2 {
private volatile static Singleton2 singleton; // 5
private Singleton2() {
System.out.println("懒汉式单例初始化!");
}
public static Singleton2 getInstance () {
if(singleton ==null) { // 1
synchronized(Singleton2.class) { // 2
if(singleton == null) { // 3
singleton = new Singleton2(); //4
}
}
}
return singleton;
}
}
Нулевое суждение в коде 1 заключается в сокращении использования методов синхронизации и повышении эффективности.
Блокировка и оценка в коде 2 и 3 предназначены для предотвращения повторного создания экземпляров синглетонов при многопоточности.
Нестабильность в коде 5 предназначена для предотвращения переупорядочения инструкций в коде 4 при многопоточности.
метод тестирования
Создайте тестовый тестовый класс
public class Test {
public static void main(String[] args) throws IOException {
// 懒汉式
Singleton1 singleton1 = Singleton1.getInstance();
// 饿汉式
Singleton2 singleton2 = Singleton2.getInstance();
}
}
результат операции
Судя по результатам, в этом нет ничего плохого, так что давайте попробуем добавить точку останова. Согласно предыдущим знаниям, голодный синглтон создается при загрузке класса, затем запуск основного метода должен вывести инициализацию голодного синглтона, давайте посмотрим на результаты:
public static void main(String[] args) throws IOException {
System.in.read();
// 饿汉式
Singleton1 singleton1 = Singleton1.getInstance();
// 懒汉式
Singleton2 singleton2 = Singleton2.getInstance();
}
Результат работы на этом этапе:
Нет результата, как показано на рисунке, почему не создается экземпляр синглтона Hungry Han? Получается, что голодный синглтон создается при загрузке класса, а голодный синглтон не был загружен в точке останова. Давайте подробно рассмотрим загрузку классов:
Загрузка класса делится на 5 шагов:Загрузка, проверка, подготовка, разбор, инициализация
Инициализация заключается в выполнении скомпилированного метода
Следовательно, создание объектов в «Режиме голодного человека» осуществляется на этапе инициализации загрузки класса, так когда же происходит этап инициализации загрузки класса? Спецификация jvm предусматривает, что фаза инициализации загрузки класса будет выполняться только в следующих семи случаях:
- При создании экземпляра объекта с использованием нового ключевого слова
- При установке или чтении статического поля класса (модифицированного с помощью final, за исключением статических полей, помещенных компилятором в пул констант)
- При вызове статического метода класса
- При использовании метода пакета java.lang.reflect для вызова отражения класса
- Инициализировать подкласс класса (сначала будет инициализирован родительский класс)
- При запуске виртуальной машины инициализируйте основной класс, содержащий основной метод
- При использовании поддержки динамического языка jdk1.7, если последним результатом синтаксического анализа экземпляра java.lang.invoke.MethodHandle является дескриптор метода REF_getStatic, REF_putStatic и REF_invokeStatic, а класс, соответствующий этому дескриптору метода, не был инициализирован , вам нужно сначала запустить его инициализацию.
В итоге,По сути, класс инициализируется только тогда, когда вы его каким-то образом вызываете, а не при запуске jvm, а сам jvm гарантирует, что инициализация класса выполняется только один раз.. Затем, если вы не используете этот объект singleton, в памяти вообще нет объекта экземпляра Singleton, что совпадает с «ленивым режимом».тот же эффект.
Конечно, также существует вероятность того, что в классе синглтона помимо метода getInstance() есть еще какие-то статические методы, так что при вызове других статических методов экземпляр тоже будет инициализирован, но это легко решается, просто добавитьвнутренний классПросто делать:
public class Singleton {
private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance () {
return LazyHolder.INSTANCE;
}
}
Суммировать
Заключение в Интернете вообще говорит о том, что синглтон слишком рано занимает ресурсы, и рекомендуется «ленивый режим», но они игнорируют, когда синглтон загружается классом.После приведенного выше анализа «ленивый режим» сложен в реализации и не не имеют исключительных преимуществ.«Режим голодного человека» побеждает. Рекомендуемые сценарии использования «Режима голодного человека»:
- При наличии других статических методов в классе singleton рекомендуется использовать форму статического внутреннего класса.
- Когда в классе singleton есть только метод getInstance(), рекомендуется напрямую создать статический объект singleton.
обновить:
Что касается класса перечисления: вот тест:
public enum SingletonEnum {
INSTANCE;
public SingletonEnum getInstance() {
return INSTANCE;
}
SingletonEnum() {
System.out.println("枚举类单例实例化啦");
}
public static void test() {
System.out.println("测试调用枚举类的静态方法");
}
}
Тестовый класс:
public static void main(String[] args) throws IOException {
SingletonEnum.test();
System.in.read();
SingletonEnum singletonEnum=SingletonEnum.INSTANCE;
}
Отсюда делается вывод, что синглтон класса перечисления инициализируется при загрузке класса (вызывается статический метод), как и обычный «голодный режим». Но еще одним преимуществом enum-классов является предотвращение отражения и сериализации, так что еще раз вывод
- При наличии других статических методов в классе singleton рекомендуется использовать форму статического внутреннего класса.
- Когда в классе singleton есть только метод getInstance(), рекомендуется напрямую создать статический объект singleton.
- Когда вам нужно предотвратить уничтожение синглетонов отражением и сериализацией, рекомендуется использовать шаблон синглтона классов перечисления.