单例模式:顾名思义,就是只有一个实例的模式
问:为什么需要单例?
答:有一些对象只需要一个实例,比如线程池、缓存、注册表......如果有多个实例,可能会导致数据不一致,资源占用高等问题。
问:全局变量不就可以做到定义变量,全局使用吗?
答:全局变量存在缺点。
- 使用前初始化时就会存在这个实例,但是在某些执行过程又不需要,造成资源的浪费。
- 全局变量只是提供一个变量的全局访问,但是不能确保实例只有一个。
问:怎么实现单例模式
答:单线程的情况下可以这么做,主要三点:静态变量,私有构造器和静态获取实例的方法
public class Singleton {
// 1. 记录实例的静态变量
private static Singleton uniqueInstance;
// 2. 私有化构造器
private Singleton() {}
// 3. 静态方法实例化对象,并返回实例
public static Singleton getInstance() {
if(uniqueInstance == null) {
// 判断实例为空再允许创建
uniqueInstance = new Singleton();
}
// 返回这个全局引用
return uniqueInstance;
}
}
问:多线程下要怎么做?
答: 多线程并发时,由于线程挂起等原因,可能导致出现两个实例。解决方式有三种:
- 在 getInstance 前加 synchronized 关键字,同步这个方法。(性能要求不高的情况下)
- “急切”创建实例。在声明全局变量时就初始化好该实例。(资源不敏感的情况下)
- 双重检查加锁,在方法1的基础上,避免第一次初始化资源后,之后每次获取资源还要锁消耗的问题。
public class Singleton {
// 1. 记录实例的静态变量
private volatile static Singleton uniqueInstance;
// 2. 私有化构造器
private Singleton() {}
// 3. 静态方法实例化对象,并返回实例
public static Singleton getInstance() {
if(uniqueInstance == null) {
// 只有第一次创建对象时才需要锁
synchronized (Singleton.class) {
// 进入后二次检查,确保实例未被创建
if(uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
总结
单例模式: 确保一个类只有一个实例,并提供一个全局访问点。
- 要获取实例,通过单例类是唯一的方式。(不考虑反射等非正常手段)
- 利用延迟实例化的方式,对资源敏感的对象是很重要的。
参考链接
更多的单例模式实现方式可参考 github.com/biyunjue/le…