所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。

    实现单例模式的五种方式:

    【饿汉式】
    优点:未加锁,执行效率高。
    缺点:类加载时就初始化实例,造成内存浪费。

    1. package com.example.designPattern.singleton;
    2. /**
    3. * 单例模式 - 饿汉式
    4. */
    5. public class HungerSingleton {
    6. /**
    7. * 构造函数私有化
    8. */
    9. private HungerSingleton() {
    10. }
    11. /**
    12. * 定义对象时直接创建实例
    13. */
    14. private static final HungerSingleton instance = new HungerSingleton();
    15. /**
    16. * 提供一个公共的静态方法,返回实例对象
    17. */
    18. public static HungerSingleton getInstance() {
    19. return instance;
    20. }
    21. }

    【懒汉式】
    优点:在第一次调用才初始化,避免了内存浪费。
    缺点:对获取实例方法加锁,大大降低了并发效率。

    package com.example.designPattern.singleton;
    /**
     * 单例模式 - 懒汉式
     */
    public class LazySingleton {
    
        /**
         * 构造函数私有化
         */
        private LazySingleton() {
        }
    
        /**
         * 定义对象
         */
        private static LazySingleton instance;
    
        /**
         * 提供一个公共的静态方法,当使用时才会去创建实例,该方法使用synchronized加锁,
           来保证线程安全性
         */
        public static synchronized LazySingleton getInstance() {
            if (instance == null) {
                instance = new LazySingleton();
            }
            return instance;
        }
    }
    

    【双重检查】
    既保证了懒加载,又保证了高性能,所以推荐使用。

    package com.example.designPattern.singleton;
    /**
     * 单例模式 - 懒汉式(双重检查)
     */
    public class DoubleCheckSingleton {
    
        /**
         * 构造函数私有化
         */
        private DoubleCheckSingleton() {
        }
    
        /**
         * 这里用volatile修饰的作用禁止指令重排序优化:
         *
         *  因为instance = new Singleton()这行代码并不是一个原子指令,而是分为三个操作
         *    1、分配对象内存
         *    2、调用构造器方法,执行初始化
         *    3、将对象引用赋值给变量
         *  虚拟机实际运行时,以上指令可能发生重排序。以上代码2、3可能发生重排序,但是并不会
         *  重排1的顺序。也就是说1这个指令都需要先执行,因为2、3指令需要依托1指令执行结果。
         *
         *  如果线程1获取到锁进入创建对象实例,这个时候发生了指令重排序。
         *  当线程1执行t1和t3操作,线程2刚好进入,由于此时对象已经不为null,所以线程2可以
         *  自由访问该对象。然后该对象还未初始化,所以线程2访问时将会发生异常。
         */
        private static volatile DoubleCheckSingleton instance;
    
        /**
         * 双重校验锁
         */
        public static DoubleCheckSingleton getInstance() {
            // 第一个判空作用是只有不为空时才需要加锁,不加的话,每次都要加锁,影响效率
            if (instance == null) {
                synchronized (DoubleCheckSingleton.class) {
                    // 第二个判空作用是为了防止并发操作下有可能其他线程获取过锁,
                    // 已经初始化过变量
                    if (instance == null) {
                        instance = new DoubleCheckSingleton();
                    }
                }
            }
            return instance;
        }
    }
    

    【静态内部类】
    该模式利用了静态内部类延迟初始化的特性,来达到与双重检查方式一样的功能。由于需要借助辅助类,并不常用。

    package com.example.designPattern.singleton;
    /**
     * 单例模式 - 静态内部类
     */
    public class InnerClassSingleton {
    
        /**
         * 构造函数私有化
         */
        private InnerClassSingleton() {
        }
    
        /**
         * 定义一个私有静态内部类,利用静态内部类特点实现延迟加载,效率高
         */
        private static class SingletonInstance {
            private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
        }
    
        /**
         * 提供一个公共的静态方法,返回实例对象
         */
        public static InnerClassSingleton getInstance() {
            return SingletonInstance.INSTANCE;
        }
    }
    

    【枚举】
    该方式利用了枚举类的特性,不仅能避免线程同步问题,还防止反序列化重新创建新的对象。但由于这种编码方式还不能适应,所以实际工作中很少使用。

    package com.example.designPattern.singleton;
    /**
     * 单例模式 - 枚举
     */
    public enum EnumSingleton {
        INSTANCE;
    }