设计模式之单例模式(二)

「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战」。

上文中我们已经对单例模式进行了简单的了解,并用小案例进行了实现,本文我们将讨论一下单例模式的几种实现方式,以便大家对其有更加深入的理解。

实现方式

一、饿汉式(开发用这种方式)——拿空间换时间

饿汉式最明显的标志就是在类创建的同时就已经创建好一个静态的对象供系统使用,以后不在改变。这种方式比较常用,但容易产生垃圾对象。它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。

优点:没有加锁,执行效率会提高。

缺点:类加载时就初始化,浪费内存。

class Singleton {
    //1,私有构造函数,为了让外界不能创建Singleton的对象
    private Singleton(){}
    //2,创建本类对象(外界不能创建,但是本类可以创建)
    /*加private 是为了不让外界随意的更改对象s 。 加static 是为了让他是静态变量优先于
    对象而存在,当用类名调用getInstance()时候,如果s前面不加static是不可以的,因为静态
    的不能直接访问非静态的*/
    private static Singleton s = new Singleton();	
    //3,对外提供公共的获取方法,获取唯一的 s 对象
    public static Singleton getInstance() {	
        return s;	
    }
}
复制代码

二、懒汉式(线程不安全)

这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。

public class Singleton {  
    //1,私有构造函数
    private Singleton (){}  
  	//2,声明一个本类的引用
    private static Singleton instance;  
    //3,对外提供公共的访问方法
    public static Singleton getInstance() {  
    	if (instance == null) {  
        	instance = new Singleton();  
    	}  
    	return instance;  
    }  
}
复制代码

三、懒汉式(线程安全)——拿时间换空间

这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。

优点:第一次调用才初始化,避免内存浪费。

缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。

class Singleton {
    //1,私有构造函数
    private Singleton(){}
    //2,声明一个本类的引用
    private static Singleton s;
    //3,对外提供公共的访问方法
    public static sychronized Singleton getInstance() {
        if(s == null)
            //线程1,线程2
            s = new Singleton();
        return s;
    }
}
复制代码

四、双检锁/双重校验锁(DCL,即 double-checked locking)

这种方式采用双锁机制,安全且在多线程情况下能保持高性能。getInstance() 的性能对应用程序很关键。

public class Singleton {  
 	//volatile关键字会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getInstance() {  
        if (singleton == null) {  
            synchronized (Singleton.class) {  
                if (singleton == null) {  
                    singleton = new Singleton();  
                }  
            }  
        }  
        return singleton;  
    }  
}
复制代码

本文的四种方式大家可以手动练习一下,毕竟光看的话容易混淆。如果你有不同的意见或者更好的idea,欢迎联系阿Q,添加阿Q可以加入技术交流群参与讨论呦!