欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

设计模式之单例模式

程序员文章站 2024-01-20 12:57:34
...

单例模式的介绍
1.什么是单例模式?
实现1个类只有1个实例化对象并提供一个全局访问点
2.单例模式的作用?
保证1个类只有1个对象,降低对象之间的耦合度,减少对象的创建减少系统开销。
3.实现单例的几种常用方式。

  1.饿汉式单例模式
  public class SingleInstance {
    //饿汉式单例模式  类被加载的时候就初始化一个实例化对象
    private static SingleInstance mSingleInstance = new SingleInstance();
    //私有构造方法,禁止其他地方通过new关键字来创建实例化对象
    private SingleInstance() {

    }
    //通过静态的方法将这个对象实例  获得该实例化对象
    public static SingleInstance getInstance(){
        return mSingleInstance;
    }
}

外部需要使用这个单例对象的时候只需要SingleInstance.getInstance();即可。这种方式在加载类的时候就把这个对象给创建出来了也有一定的缺陷就是我没用单例的时候就已经加载了,所以造成了内存的浪费(额不过既然你都已经设计了这个模式,我想你一定会用到的吧!!!),但是由于类只会被加载一次所以线程安全。
2.懒汉式单例模式

public class SingleInstance {
    //懒汉式单例模式
    private static SingleInstance mSingleInstance = null;
    //私有构造方法,禁止其他地方通过new关键字来创建实例化对象
    private SingleInstance() {

    }
    //通过静态的方法将这个对象实例  获得该实例化对象
    public static SingleInstance getInstance(){
        //判断是否为空,避免重复创建
        if (mSingleInstance == null){
            mSingleInstance = new SingleInstance();
        }
        return mSingleInstance;
    }
}

该方式在类初始化的是时候并不会直接创建实例,而是在外部调用getInstance()的时候去判断是否存在实例,若不存在则创建否则直接返回实例。但是这种方式有个致命的缺点就是线程不够安全。想象这样一个场景,A线程调用getInstance走到mSingleInstance = new SingleInstance();但是还没有得到实例(需要一定的时间)B也调用getInstance那么此时mSingleInstance为空,B也会新建一个实例,这样单例模式就失效了。
3.改进懒汉式单例模式

public class SingleInstance {
    //懒汉式单例模式
    private static SingleInstance mSingleInstance = null;
    //私有构造方法,禁止其他地方通过new关键字来创建实例化对象
    private SingleInstance() {

    }
    //通过静态的方法将这个对象实例  获得该实例化对象
    public static synchronized  SingleInstance getInstance(){
        //判断是否为空,避免重复创建
        if (mSingleInstance == null){
            mSingleInstance = new SingleInstance();
        }
        return mSingleInstance;
    }
}

使用同步锁 synchronized锁住 创建单例的方法 ,防止多个线程同时调用,从而避免造成单例被多次创建 ,每次访问都要进行线程同步(即 调用synchronized锁),造成过多的同步开销(加锁 = 耗时、耗能)实际上只需在第1次调用该方法时才需要同步,一旦单例创建成功后,就没必要进行同步
继续改进

	public class SingleInstance {
    //懒汉式单例模式
    private static SingleInstance mSingleInstance = null;
    //私有构造方法,禁止其他地方通过new关键字来创建实例化对象
    private SingleInstance() {

    }
    //通过静态的方法将这个对象实例  获得该实例化对象
    public static  SingleInstance getInstance(){
        //判断是否为空,避免重复创建
        if (mSingleInstance == null){①
            synchronized (SingleInstance.class){
                if (mSingleInstance == null){ ②
                    mSingleInstance = new SingleInstance();
                }
            }
        }
        return mSingleInstance;
    }
}

这里用了双重检查机制。双重if判断
校验锁1:第1个if
作用:若单例已创建,则直接返回已创建的单例,无需再执行加锁操作
即直接跳到执行 return ourInstance
校验锁2:第2个 if
作用:防止多次创建单例问题
原理:线程A调用newInstance(),当运行到②位置时,此时线程B也调用了newInstance()因线程A并没有执行instance = new Singleton();,此时instance仍为空,因此线程B能突破第1层 if 判断,运行到①位置synchronized中的A线程执行完毕当线程A释放同步锁时,单例已创建,即instance已非空 此时线程B 从①开始执行到位置②。此时第2层 if 判断 = 为空(单例已创建),因此也不会创建多余的实例

public class SingleInstance {

    private static class Single{
        private static SingleInstance mSingleInstance = new SingleInstance();
    }
    private SingleInstance() {

    }
    //通过静态的方法将这个对象实例  获得该实例化对象
    public static  SingleInstance getInstance(){
       return Single.mSingleInstance;
    }
}

调用过程说明:

  1. 外部调用类的newInstance()

  2. 自动调用Singleton2.ourInstance
    2.1 此时单例类Singleton2得到初始化
    2.2 而该类在装载 & 被初始化时,会初始化它的静态域,从而创建单例;
    2.3 由于是静态域,因此只会JVM只会加载1遍,Java虚拟机保证了线程安全性

  3. 最终只创建1个单例

    枚举(头疼并不习惯)

  public enum SingleInstance{

    //定义1个枚举的元素,即为单例类的1个实例
    INSTANCE;

    // 隐藏了1个空的、私有的 构造方法
    // private SingleInstance () {}

}

// 获取单例的方式:
SingleInstance singleInstance = SingleInstance.INSTANCE;

推荐使用静态内部类的方式!!!

参考文章:单例模式(Singleton)- 最易懂的设计模式解析https://www.jianshu.com/p/b8c578b07fbc

相关标签: 单例设计模式