单例模式
2020-11-24 本文已影响0人
wendy__xu
单例模式
定义:
当系统中只需要一个实例或者一个全局访问点的时候可以使用单例模式
- 优点:节省系统创建对象的资源,提高了系统效率,提供了统一的访问入口,可以严格控制用户对该对象的访问。
- 缺点:只有一个对象,积累的指责过重,违背了单一指责原则。构造方法为private,无法继承,扩展性较差。
单例模式的实现方式:
- 懒汉式
- 双层校验锁单例
- 容器单例
- 静态内部类单例
- 枚举单例
其中静态内部类单例和枚举单例都是单例模式的最佳的实现,但是出于便利性的考量,双层校验锁单例使用较为广泛,如下所示:
public class DoubleCheckSingleton{
// volatile关键字保证了:instance实例对于所有线程都是可见的,禁止了instance操作指令重排序
public volatile static DoubleCheckSingleton instance;
private DoubleCheckSingleton{}
public DoubleCheckSingleton getInstance(){
//第一次校验,防止不必要的同步
if(instance != null){
//synchronized关键字加锁,保证每次只有一个线程执行对象初始化操作
synchronized(DoubleCheckSingleton.class){
//第二次校验,进行判空,如果为空则执行初始化
if(instance == null){
instance = DoubleCheckSingleton()
}
}
}
return instance;
}
}
volatile关键解析
Q1:什么是禁止指令重排序?
我们首先看下创建一个对象在汇编指令里会做哪些事情,以上面为例:
- 给我们instance分配内存。
- 调用DoubleCheckSingleton()构造方法。
- 将构造的对象赋值给instance。
但是在真正执行的时候,java编译器是允许指令乱序执行的(编译优化),所以上述3步的顺序得不到保证,有可能是132,试想一下,如果线程A没有执行第2步,先执行了3步,而恰在此时,线程B取走了instance对象,在使用instance对象时就会有问题,双层校验锁单例失败,而volatile关键字可以禁止指令重排序从而解决这个问题。
单例模式的另一个问题就是多进程的情况下的失败问题,因为JVM里的单例是基于一个虚拟机进程的,这个时候通常的做法就是让这个单例支持跨进程调用,这个在Android里一般用AIDL实现。