设计模式--单例(Singleton Pattern)

定义

单例模式: 确保某一个类只有一个实例,并且自行实例化并向整个系统提供这个实例。bash

使用场景

避免产生多个对象消耗过多的资源,或者某种类型的对象只有一个。函数

一、饿汉模式

public static class Singleton {
    private static final instance Singleton = new Singleton();
    
    private Singleton() { // 私有构造函数
    }
    
    public static Singleton getInstance() {
        return instance;
    }
}
复制代码

instance是静态成员变量,在声明时就被实例化。优化

二、懒汉模式

public static class Singleton {
    private static final instance Singleton = null;
    
    private Singleton() { // 私有构造函数
    }
    
    public static synchronized Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton()
        }
        return instance;
    }
}
复制代码

instance只是在初次调用时初始化,synchronized,每次调用getInstance()方法都会进行同步,这样会消耗没必要要的资源,这也是懒汉模式最大的问题。ui

三、Double Check Lock(DCL)

public static class Singleton {
    private static final instance Singleton = null;
    
    private Singleton() { // 私有构造函数
    }
    
    public static Singleton getInstance() {
        if(instance == null) {
            synchronized(Singleton.class) {
                if(instance == null) {
                    instance = new Singleton()
                }
            }
        }
        return instance;
    }
}
复制代码

DCL有两次判空,第一次判空,是为了不没必要要的同步;第二次判空,是为了初次初始化时,只初始化一次。

spa

instance = new Singletone(); 这行代码最终会编译成多条汇编指令,它大体作了三件事:线程

  1. 给Singleton的实例分配内存;
  2. 调用Singleton()的构造函数,初始化成员字段;
  3. 将instance对象指向分配的内存空间(此时instance再也不是null了);

DCL失效状况:
因为Java编译器容许处理器乱序执行,以及JDK1.5以前JMM(Java Memory Model)中Cache、寄存器到内存回写顺序的规定,上面第二和第三的顺序是没法保证的。也就是说有可能JVM会为新的Singleton实例分配空间,而后直接赋值给instance成员,而后再去初始化这个Singleton实例,这样就可能出错了。咱们以A、B两个线程为例:
code

  1. A、B线程同时进入了第一个if判断
  2. A首先进入synchronized块,因为instance为null,因此它执行instance = new Singleton();
  3. 因为JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),而后A离开了synchronized块。
  4. B进入synchronized块,因为instance此时不是null,所以它立刻离开了synchronized块并将结果返回给调用该方法的程序。
  5. 此时B线程打算使用Singleton实例,却发现它没有被初始化,因而错误发生了。

在JDK1.5以后,SUN公司调整了JVM,增长了volatile关键字,只需将instance的定义改为private volatile static Singleton instance = null就能够保存instance对象每次都是从主内存总读取。对象

4.静态内部类单例模式

public static class Singleton {
    private Singleton() { // 私有构造函数
    }
    
    private static class SingletonHolder {
        private static final instance Singleton = new Singleton();
    }
    
    public static synchronized Singleton getInstance() {
        return SingletonHolder.instance;
    }
}
复制代码

当第一次加载Singleton类时,并不会初始化,只有当第一次调用getInstance()方法,会致使虚拟机加载SingletonHolder类,此时才是初始化。利用的Java类加载机制接口

不一样的类装载器,可能会致使产生多个单例对象:内存

public static Class getClass(String className) throws ClassNotFoundException {
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    if (cl == null) {
        cl = Singleton.class.getClassLoader();
    }
    return cl.loadClass(className);
}
复制代码

若是Singleton实现了Serializable接口,反序列化时可能产生多个实例对象:

private Object readResolve() { // 重写readResolve方法
    return instance;
}
复制代码
相关文章
相关标签/搜索