设计模式——单例模式

单例模式

定义:Ensure a class has only one instance, and provide a global point of access to it. (确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例。)

优势:通过使用 private 的构造函数确保在一个 application 中只产生一个实例,从而减少内存开支,特别是一个对象需要被频繁的创建和销毁时。

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

1、懒汉式(要别人叫才吃饭,吃饭不积极脑子有问题)

public class Singleton{
    private static Singleton instance=null;
    
    private Singleton(){
    }
    
    public static synchronized Singleton getInstance(){
        if(instance==null){
               instance=new Singleton;
        }
        return instance;
    }
    
}

2、饿汉式(人家还没叫你吃饭你就开始吃上了)

public class Singleton{

    // initailzed during class loading
    private static final Singleton instance = new Singleton();
    
    //to prevent creating another instance of Singleton
    private Singleton(){
        //do something
    }
    
    public static Singleton getInstance(){
        return instance;
    }
}
复制代码
因为单例是静态的final变量,所以当类第一次加载到内存中的时候就初始化了,那么创建的实例固然是线程安全的。

3、静态内部类实现单例模式

public class Singleton {
    public static class  LazyHolder{
        private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton(){ }
    public static Singleton getInstance(){
        return LazyHolder.INSTANCE;
    }
}

这里有几个需要注意的点:

1.从外部无法访问静态内部类LazyHolder,只有当调用Singleton.getInstance方法的时候,才能得到单例对象INSTANCE。

2.INSTANCE对象初始化的时机并不是在单例类Singleton被加载的时候,而是在调用getInstance方法,使得静态内部类LazyHolder被加载的时候。因此这种实现方式是利用classloader的加载机制来实现懒加载,并保证构建单例的线程安全。

3.静态内部类的实现方式也存在单例模式共同的问题:无法防止利用反射来重复构建对象

4、synchronized、volatile实现单例模式(双重锁)

public class Singleton {
    private Singleton() {}  //私有构造函数
    private volatile static Singleton instance = null;  //单例对象
    //静态工厂方法
    public static Singleton getInstance() {
          if (instance == null) {      //双重检测机制
         synchronized (Singleton.class){  //同步锁
           if (instance == null) {     //双重检测机制
             instance = new Singleton();
                }
             }
          }
          return instance;
      }
}

经过volatile的修饰,当线程A执行instance = new Singleton的时候,JVM执行顺序是什么样?始终保证是下面的顺序:

memory =allocate(); //1:分配对象的内存空间

ctorInstance(memory); //2:初始化对象

instance =memory; //3:设置instance指向刚分配的内存地址

volatile关键字不但可以防止指令重排,也可以保证线程访问的变量值是主内存中的最新值

如此在线程B看来,instance对象的引用要么指向null,要么指向一个初始化完毕的Instance,而不会出现某个中间态,保证了安全。

5、用枚举实现单例模式:

public enum SingletonEnum {
    INSTANCE;
}

这种方法利用enum语法糖,JVM会阻止反射获取枚举类的私有构造方法。

小总结: https://gitee.com/dongldl/my-cdn/raw/master/image/image-20200801221520120.png

end

评论