单例模式

01背景以我们常用办公软件WPS为例,我们使用的时候期望点击一次工具栏弹出一个对话框,再次点击的时候仍然是当前对话框,而不是出现多个对话框 。反映到编程中,其实就是对话框只被实例化一次,这就是单例模式的一个应用场景 。02概述单例模式(Singleton Pattern):单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法 。根据定义可知单例模式的要点有三个:
1、某个类只能有一个实例;
2、它必须自行创建这个实例;
3、它必须自行向整个系统提供这个实例 。
单例模式是一种对象创建型模式 。单例模式又名单件模式或单态模式 。
03实现3.1 饿汉式
饿汉法就是在第一次引用该类的时候就创建对象实例,而不管实际是否需要创建 。一上来就先实例化,但若类没有使用的话,就有点浪费资源类 。public class Singleton {private static instance = new Singleton();private Singleton() {}public static getInstance() {return m_instance;}}优点:这样做的好处是编写简单,无需关注线程安全问题 。
缺点:
1、会生产出过多的实例对象,无论你是否要使用他们 。
2、无法做到延迟创建对象,但是我们很多时候都希望对象可以尽可能地延迟加载,从而减小负载,所以需要懒汉法 。
3.2 懒汉式
饿汉式(又称饱汉模式),很饱不着急,延迟加载,啥时候用啥时候创建实例,存在线程安全问题 。
实例在开始时为空,第一次加载后才实例化 。可节约一些资源,但在并发时有可能出现多个单例 。
优点:延时加载,用的时候才会生产对象 。
缺点:存在线程安全问题,需要保证同步,付出效率的代价 。
1、单线程实现
这种写法是最简单的,由私有构造器和一个公有静态工厂方法构成,在工厂方法中对singleton进行null判断,如果是null就new一个出来,最后返回singleton对象 。
public class Singleton {private static Singleton singleton = null;//声明private Singleton(){}public static Singleton getSingleton() {if(singleton == null) singleton = new Singleton();//实例化return singleton;}}优点:延迟加载,资源利用率高,不执行getInstance()就不会被实例化,可以执行该类的其他静态方法 。
缺点:第一次加载时不够快,线程不安全,多线程使用不必要的同步开销大 。
2、多线程实现
1)直接加锁Singleton* Singleton::getInstance() {Lock lock;if (nullptr == m_instance) {m_instance = new Singleton();}return m_instance;}优点:线程安全 。
缺点:代价太高(整个判空和申请阶段全部都加锁,一劳永逸,但是效率低,锁范围太大) 。
2)双检查锁
双重锁模式,是饱汉模式的优化,进行双重判断,当已经创建过实例对象后就无需加锁,提高效率 。也是一种推荐使用的方式 。
Singleton* Singleton::getInstance() {if (nullptr == m_instance) {Lock lock;if (nullptr == m_instance) {m_instance = new Singleton();}}return m_instance;}只有对象为空的时候才加锁,加完锁后再判空,防止在加锁的过程中被另一个线程调用new,即进行双检查:第一次检查是避免代价过高的问题,第二次检查是防止多线程问题 。
注:可以看出来与直接加锁相比,锁的范围缩小了,效率得以提升,同时通过在申请实例前加锁保证线程安全 。

推荐阅读