设计模式学习-单例模式

120 阅读3分钟
一、定义

单例模式(Singleton Pattern)是 Java 中属于创建型模式,确保一个类只有一个实例,并提供一个全局访问点。

二、分类

单例设计模式分类两种:

饿汉式:类加载时实例对象就会被创建。

懒汉式:类加载时实例对象不会被创建,首次使用该对象时才会创建。

三、实现方法
  1. 饿汉式(静态常量、静态代码块)

    public class Singleton {
    ​
        // 私有构造方法
        private Singleton(){}
    ​
        // 创建静态对象
        private static final Singleton singleton = new Singleton();
    ​
        // 提供全局访问
        public static Singleton getSingleton(){
            return singleton;
        }
    }
    ​
    public class Singleton {
    ​
        // 私有化构造方法
        private Singleton() {}
    ​
        // 声明对象
        private static Singleton singleton;
    ​
        // 静态代码块初始化对象
        static {
            singleton = new Singleton();
        }
    ​
        // 提供全局访问
        public static Singleton getSingleton(){
            return singleton;
        }
    }
    

    特点: 类初始化时快速实例化对象,避免了线程安全问题;如果该实例很大,而不被使用则会造成资源浪费。

  2. 懒汉式(线程不安全)

    public class Singleton {
    ​
        // 私有构造方法
        private Singleton() {}
    ​
        // 声明对象变量
        private static Singleton singleton;
    ​
        // 提供全局访问,线程不安全(多线程执行singleton可能都为null,造成多个实例创建)
        public static Singleton getSingleton(){
            // 第一次使用时创建
            if (singleton == null){
                singleton = new Singleton();
            }
            return singleton;
        }
    }
    

    特点: 延迟初始化实例,解决资源浪费问题;但是多线程环境下,可能造成多个实例。可承担线程安全问题情况下选择使用。

  3. 懒汉式(线程安全,同步方法)

    public class Singleton {
    ​
        // 私有构造方法
        private Singleton() {}
    ​
        // 声明对象变量
        private static Singleton singleton;
    ​
        // 提供全局访问,同步方法(线程安全)
        public synchronized static Singleton getSingleton(){
            // 第一次使用时创建
            if (singleton == null){
                singleton = new Singleton();
            }
            return singleton;
        }
    }
    

    特点: 解决多线程造成多实例问题;仅在第一次调用时会出现线程安全问题,使用synchronized方法效率性能低下。不介意性能的情况下使用。

  4. 懒汉式(双重检查)

    public class Singleton implements Serializable {
    ​
        // 私有构造方法
        private Singleton() {}
    ​
        // 对象变量,volatile保证可见性,有序性。
        private static volatile Singleton singleton;
    ​
        // 提供全局访问
        public static Singleton getSingleton(){
    ​
            // 第一次判断,如果对象不为null,直接返回
            if (singleton == null){
                synchronized (Singleton.class){
                    // 第二次判断
                    if (singleton == null){
                        singleton = new Singleton();
                    }
                }
            }
            return singleton;
        }
    }
    

    特点: 利用双重检查加锁,首先检查实例是否存在,如果不存在,才进行同步创建,这样一来只有第一次会同步,后续直接返回。解决线程和性能问题。

  5. 静态内部类

    public class Singleton {
    ​
        // 私有
        private Singleton(){}
    ​
        // 定义静态内部类
        private static class SingletonHolder{
            // 在内部类中声明并且初始化外部类的对象
            private static final Singleton SINGLETON = new Singleton();
        }
    ​
        // 提供全局访问
        public static Singleton getInstance(){
            return SingletonHolder.SINGLETON;
        }
    }
    

    特点: 第一次加载Singleton类时不会去初始化INSTANCE,只有第一次调用getInstance,虚拟机加载SingletonHolder并初始化INSTANCE,这样不仅能确保线程安全,也能保证 Singleton 类的唯一性。静态内部类单例模式是一种优秀的单例模式,是开源项目中比较常用的一种单例模式。在没有加任何锁的情况下,保证了多线程下的安全,并且没有任何性能影响和空间的浪费。

  6. 枚举

    public enum Singleton {
        INSTANCE
    }
    

    特点: 枚举方式属于饿汉式单例模式,不可被破坏。

四、单例破坏
  1. 类进行序列化再进行反序列化造成对象不一致。
  2. 通过反射取消访问检查再通过构造器创建对象。
  3. 枚举实现方式不可被破坏。
五、总结
  1. 单例模式确保程序中一个类只有一个实例和提供一个访问这个实例的全局点。

  2. 确定性能和资源条件的情况下,选择适当的方式来实现单例模式。

    注意:

    (1) Java1.5以前的版本双重检查加锁方式会失效.

    (2) 多个类加载器可能导致单例失效产生多个实例。

    (3) Java1.2之前单例模式没有全局引用时,会被垃圾回收。