单例模式:Singleton Pattern


原话:Ensure a class has only one instance ,and provide a global point of access to it.

直译:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

定义:

一个类只能产生一个对象。(我是皇帝我独苗)

方法:

把该类的构造函数设置为private(私有访问),外部无法访问该构造函数 且又没有办法调用系统默认构造函数,就可以禁止外部创建对象。

示例类图:

单例模式通用类图

示例代码:
class Singleton{
	private static final Singleton singleton = new Singleton();
	//限制产生多个对象

	private Singleton(){
		//构造函数
	}

	//通过该方法获得实例对象
	public static Singleton getSingleton(){
		return singleton;
	}

	//其他方法,尽量是static
	public static void doSomeThing(){
		//...
	}
}
优点:
  1. 只有一个实例,减少了内存开支
  2. 减少了系统的性能开销
  3. 避免了对同一个资源文件的同时写操作
  4. 可以在系统内设置全局访问点,优化和共享资源访问,负责所有数据表的映射处理
缺点:
  1. 没有借口,扩展很困难
  2. 对测试不利,单例模式没有完成,不能进行测试
  3. 与单一职责有冲突,一个类应该只实现一个逻辑,而不关心它是否是单例的
应用场景:
  1. 要一个类有且仅有一个对象
  2. 需要一个共享访问点或共享数据
  3. 创建一个对象需要的资源过多
  4. 需要定义大量的静态常量和方法
注意事项:

在高并发的情况下,需要考虑单例模式的线程同步问题, 可能会出现两个对象,破坏了最初的预期

双重检验加锁:强化单例加锁
//强化单例:
public static Singleton getInstance(){
    if(instance == null){
        synchronized (Singleton.class){
            if(instance == null){
                instance = new Singleton();
            }
        }
    }
    return instance;
}
  • 线程1进入方法体,满足条件instance == null,进入synchronized块
  • 线程间抢占cpu资源,线程1被线程2预占,线程2进入方法体
  • 此时方法还没执行完,对象还没有创建出来instance依然为null
  • 线程2试图获取锁资源,但线程1持有该锁,于是线程2阻塞,线程2被线程1预占
  • 线程1继续执行,此时instance 仍然为null,创建Singleton实例并将其引用赋值给instance,方法返回该实例对象的引用,退出方法
  • 线程1被线程2预占,线程2获取锁并检查 instance是否为 null.由于instance 已经被赋值,所以不会创建第二个 Singleton 对象.
  • 这样一套双重检查加锁机制实现了比普通单例模式更高效率的逻辑

其中,双重检索加锁:参考了某位简书博主