单例模式
单例模式: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(){
//...
}
}
优点:
- 只有一个实例,减少了内存开支
- 减少了系统的性能开销
- 避免了对同一个资源文件的同时写操作
- 可以在系统内设置全局访问点,优化和共享资源访问,负责所有数据表的映射处理
缺点:
- 没有借口,扩展很困难
- 对测试不利,单例模式没有完成,不能进行测试
- 与单一职责有冲突,一个类应该只实现一个逻辑,而不关心它是否是单例的
应用场景:
- 要一个类有且仅有一个对象
- 需要一个共享访问点或共享数据
- 创建一个对象需要的资源过多
- 需要定义大量的静态常量和方法
注意事项:
在高并发的情况下,需要考虑单例模式的线程同步问题, 可能会出现两个对象,破坏了最初的预期
双重检验加锁:强化单例加锁
//强化单例:
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 对象.
- 这样一套双重检查加锁机制实现了比普通单例模式更高效率的逻辑
其中,双重检索加锁:参考了某位简书博主