Singleton pattern - inner class

  1. Why is the singleton pattern for static inner classes the most recommended?

  2. How to guarantee singleton in case of reflection?

  3. How to guarantee singleton in deserialization?

    In response to the above three problems, this article introduces the last singleton design pattern in a step-by-step manner, hoping to be helpful to everyone.

    singleton design pattern

    1. Hungry Chinese

    In fact, everyone knows this, not much to say, on the code.

    package singleton;
    public class Singleton1 {
        private static Singleton1 instance = new Singleton1();
        private Singleton1 (){}
        public static Singleton1 getInstance() {
            return instance;
        }
    }
    The advantage is thread safety, the disadvantage is obvious, the object is instantiated when the class is loaded, which wastes space. Therefore, the lazy singleton pattern was proposed.

    2. Lazy Man

    (1) Lazy Man v1


    public class LazySingleton1 {
        private static LazySingleton1 instance;
        private LazySingleton1 (){}
        public static LazySingleton1 getInstance() {
            if (instance == null) {
                instance = new LazySingleton1();
            }
            return instance;
        }
    However, this version of the thread is not safe, so for thread safety, the synchronized modifier is added to the getInstance() method, so the getInstance() method is as follows
    public static synchronized LazySingleton1 getInstance() {
            if (instance == null) {
                instance = new LazySingleton1();
            }
            return instance;
        }

    然而,将synchronized加在方法上性能大打折扣(syncrhonized会造成线程阻塞),于是乎又提出一种双重校验锁的单例设计模式,既保证了线程安全,又提高了性能。双重校验锁的getInstance()方法如下所示
    public static LazySingleton1 getInstance() {
            if (instance == null) {  
                synchronized (LazySingleton1.class) {  
                if (instance == null) {  
                    instance = new LazySingleton1();  
                    }  
                }  
            } 
            return instance;
        }

    (2)懒汉式v2

    懒汉式v1的最后一个双重校验锁版,不管性能再如何优越,还是使用了synchronized修饰符,既然使用了该修饰符,那么对性能多多少少都会造成一些影响,于是乎懒汉式v2版诞生。不过在讲该版之前,我们先来复习一下内部类的加载机制,代码如下

    public class OuterTest {
        static {
            System.out.println("load outer class...");
        }
        // 静态内部类
        static class StaticInnerTest {
            static {
                System.out.println("load static inner class...");
            }
            static void staticInnerMethod() {
                System.out.println("static inner method...");
            }
        }
        public static void main(String[] args) {
            OuterTest outerTest = new OuterTest(); // 此刻其内部类是否也会被加载?
            System.out.println("===========分割线===========");
            OuterTest.StaticInnerTest.staticInnerMethod(); // 调用内部类的静态方法
        }
    }
    输出如下
    load outer class...
    ===========分割线===========
    load static inner class...
    static inner method

    因此,我们有如下结论

    1. 加载一个类时,其内部类不会同时被加载。

    2. 一个类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生。。

    基于上述结论,我们有了懒汉式V2版,代码如下所示

    package singleton;
    public class LazySingleton2 {
        private LazySingleton2() {
        }
        static class SingletonHolder {
            private static final LazySingleton2 instance = new LazySingleton2();
        }
        public static LazySingleton2 getInstance() {
            return SingletonHolder.instance;
        }
    }

    由于对象实例化是在内部类加载的时候构建的,因此该版是线程安全的(因为在方法中创建对象,才存在并发问题,静态内部类随着方法调用而被加载,只加载一次,不存在并发问题,所以是线程安全的)。

    另外,在getInstance()方法中没有使用synchronized关键字,因此没有造成多余的性能损耗。当LazySingleton2类加载的时候,其静态内部类SingletonHolder并没有被加载,因此instance对象并没有构建。

    而我们在调用LazySingleton2.getInstance()方法时,内部类SingletonHolder被加载,此时单例对象才被构建。因此,这种写法节约空间,达到懒加载的目的,该版也是众多博客中的推荐版本

    ps:其实枚举单例模式也有类似的性能,但是因为可读性的原因,并不是最推荐的版本。


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325615233&siteId=291194637