# 单件模式
# 什么是单件模式
- 单件模式:确保一个类只有一个实例,并提供一个全局访问点;
- 有些对象我们只需要一个实例,例如线程池,缓存,对话框,全局设置对象,设备的驱动程序,等等;
- 我们并不通过设置 “全局变量” 来实现单件模式;
- 因为如果把对象赋值给一个全局变量,那么我们就要在程序一开始就创建好对象;
- 如果对象创建非常浪费资源(内存,时间)。那么这样就会很影响性能;
- 通过单件模式,我们在需要时才创建对象;
- 并且我们需要保证,这个实例是唯一的。不能被创建第二次;
# 经典的单件模式实现
- 定义一个单例类;
- 里面用一个和单例类同类型的私有静态属性,来储存单例的唯一实例;
- 单例类的构造函数必须是私有的,这样外部就不可以调用构造函数去创建其他的实例了;
getInstance
是一个公开的静态方法:- 它里面会先判断实例是否已经被创建,如果没有就创建一个,并且返回实例给外部;
- 如果已经创建了实例,就直接返回实例;
public class Singleton {
private static Singleton uniqueInstance = null;
private Singleton() {}
// 只有 getInstance 是 public 的,
// 用户通过它来获取实例
public static Singleton getInstance() {
if(uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
# 处理多线程的单件模式
- 上面的单例实现方法,在多线程中可能会出现问题;
- 多线程的话,对于当前实例的状态是不确定的;
- 比如可能会产生两个对象开始发现都为空,于是在 JVM 中就新建了两个对象;
# 通过 synchronized 关键字解决
- 在新建对象的方法上加上
synchronized
关键字,可以保证不会有两个线程同时进入到这个方法中; - 但同步会降低性能,因为要进行判断这个方法的状态;
- 一旦这个变量已经实例化了,每次访问还是同步的话,就是一种累赘;
- 同步一个方法,会使程序运行效率下降 100 倍;
class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
# 直接实例化
- 如果创建实例并不需要消耗太多资源;
- 那么可以直接在定义类时,直接初始化实例;
- JVM 会在加载这个类时,立马创建唯一的单件实例;
class SingletonFast {
private static SingletonFast singleton = new SingletonFast();
private SingletonFast() {
}
public static SingletonFast getInstance() {
return singleton;
}
}
# 双重检查加锁
- 通过 “双重检查加锁”,我们首选检查是否实例已经创建了;
- 如果没有创建,才进行同步;
- 这样只有第一次会进行同步;
- 可以大大提升性能;
class Singleton {
//volatile关键词确保,当singleton变量被初始化成Singleton实例时,多个线程正确处理singleton变量
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;
}
}