单例模式--内部类

  1. 为什么静态内部类的单例模式是最推荐的?

  2. 如何在反射的情况下保证单例?

  3. 如何在反序列化中保证单例?

    针对上述三个问题有了这篇文章,以一种循序渐进的方式,引出最后一种单例设计模式,希望对大家能够有所帮助。

    单例设计模式

    1、饿汉式

    这种其实大家都懂,不多说,上代码。

    package singleton;
    public class Singleton1 {
        private static Singleton1 instance = new Singleton1();
        private Singleton1 (){}
        public static Singleton1 getInstance() {
            return instance;
        }
    }
    优点就是线程安全啦,缺点很明显,类加载的时候就实例化对象了,浪费空间。于是乎,就提出了懒汉式的单例模式

    2、懒汉式

    (1)懒汉式v1


    public class LazySingleton1 {
        private static LazySingleton1 instance;
        private LazySingleton1 (){}
        public static LazySingleton1 getInstance() {
            if (instance == null) {
                instance = new LazySingleton1();
            }
            return instance;
        }
    然而这一版线程是不安全的,于是乎为了线程安全,就在getInstance()方法上加synchronized修饰符,于是getInstance()方法如下所示
    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;
        }
    }

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

    扫描二维码关注公众号,回复: 150334 查看本文章

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

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

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


猜你喜欢

转载自blog.csdn.net/zhmi_1015/article/details/80210649
今日推荐