1.什么是设计模式?
设计模式好⽐象棋中的"棋谱".红⽅当头炮,⿊⽅⻢来跳.针对红⽅的⼀些⾛法,⿊⽅应招的时候有⼀ 些固定的套路.按照套路来⾛局势就不会吃亏. 软件开发中也有很多常⻅的"问题场景".针对这些问题场景.
单例模式能保证某个类在程序中只存在唯⼀⼀份实例,⽽不会创建出多个实例.
2.饿汉模式
代码语言:javascript复制class Singleton {
private static Singleton instance = new Singleton();
//在这个类被加载的时候,就会初始化这个 静态变量
public static Singleton getInstance() {
return instance;
}
private Singleton(){}
//后续再别的代码中,尝试new Singleton,就会直接编译报错
}
对于饿汉来说,getInstance 直接返回instance实例, 这个本质上就是"读操作".所以即使是多个线程同时读取一个变量,线程也是安全的
3.懒汉模式
代码语言:javascript复制class SingletonLazy {
private static SingletonLazy instance = null;
//这个指引指向唯一实例,想爱你初始化为空,而不是立即创建实例
public static SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
private SingletonLazy() {}
}
如果是首次调用getInstance, 此时引用为null,就会进入if语句,从而创建实例 如果是后续再次调用getInstance,由于instance已经不再是null,所以直接返回创建好的引用 这样设定,仍然可以保证该类是唯一实例,与此同时,创建实例的时机就不是程序驱动时,而是第一次调用getInstance的时候了
4.懒汉模式(多线程版)
代码语言:javascript复制上⾯的懒汉模式的实现是线程不安全的. 线程安全问题发⽣在⾸次创建实例时. 如果在多个线程中同时调⽤ getInstance ⽅法, 就可能导致创建 出多个实例. ⼀旦实例已经创建好了, 后⾯再多线程环境调⽤getInstance就不再有线程安全问题了(不再修改 instance 了) 而加上 synchronized 可以改善这⾥的线程安全问题.
class SingletonLazy {
private static SingletonLazy instance = null;
//这个指引指向唯一实例,想爱你初始化为空,而不是立即创建实例
private static Object locker = new Object();
public static SingletonLazy getInstance() {
synchronized (locker) {
if (instance == null) {
instance = new SingletonLazy();
}
}
return instance;
}
private SingletonLazy() {}
}
5.懒汉模式(多线程)(改进)
代码语言:javascript复制加锁 / 解锁是⼀件开销⽐较⾼的事情. ⽽懒汉模式的线程不安全只是发⽣在⾸次创建实例的时候. 因此后续使⽤的时候, 不必再进⾏加锁了. 所以这个时候可以在家一个 if 判定是否要加锁.
private static Object locker = new Object();
public static SingletonLazy getInstance() {
if (instance == null) { //第一层 if 是判定是否要加锁
synchronized (locker) {
if (instance == null) {// 第二层 if 是判定是否要创建对象
instance = new SingletonLazy();
}
}
}
return instance;
}
代码语言:javascript复制这个代码还是有些问题 指令重排序 , 是编译器优化的一种方式 , 也是引起线程安全的问题. 指令重排序是指调整原有的代码执行顺序,保证逻辑不变的前提下,提高程序效率
instance = new SingletonLazy();
这行代码可以拆成三大步骤 1.申请一段内存空间 2.在这个内存上调用构造方法,创建出这个实例 3.把这个内存地址赋值给 instance 引用变量
代码语言:javascript复制所以,解决上述问题 需要用到 volatile,它有两个功能: 1.保证内存可见性,每次访问变量必须都要重新读取内存,而不是直接优化到寄存器 / 缓存中 2.禁止指令重排序, volatile 修饰的变量的读写操作相关指令,不能被重排序的 所以最终代码为下
class SingletonLazy {
private volatile static SingletonLazy instance = null;
//这个指引指向唯一实例,想爱你初始化为空,而不是立即创建实例
private static Object locker = new Object();
public static SingletonLazy getInstance() {
if (instance == null) { //第一层 if 是判定是否要加锁
synchronized (locker) {
if (instance == null) {// 第二层 if 是判定是否要创建对象
instance = new SingletonLazy();
}
}
}
return instance;
}
private SingletonLazy() {}
}