1、模式定义
- 作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。
- 单例特别:(1)单例模式只能有一个实例。(2)单例类必须创建自己的唯一实例。(3)单例类必须向其他对象提供这一实例。
2、代码实现
(1)双重加锁方式
public class Singlnton{
private static volatile Singlnton instance;
private Singlnton(){
}
public static Singlnton getInstance(){ // 1
if(instance==null){ // 2
synchronized(Singlnton.class){ // 3
if(instance==null){ // 4
instance = new Singlnton(); // 5
}
}
}
return instance;
}
}
- volatile关键字的作用?
- 双重检锁单例模式在CPU的工作流,主要分为三步,1:分配内存对象空间。2:初始化对象。3:设置instance执行刚才分配的内存地址,注意jvm和cpu优化会指令重排,上面顺序会变成1-3-2,单线程环境下,此顺序是没有问题,2和3 前后没有依赖性,但是在多线程情况下会有这种情况,当线程A在执行第5行代码时,B线程进来执行到第2行代码。假设此时A执行的过程中发生了指令重排序,即先执行了1和3,没有执行2。那么由于A线程执行了3导致instance指向了一段地址,所以B线程判断instance不为null,会直接跳到第6行并返回一个未初始化的对象。volatile保持指令的有序性,能够有效禁止指令重排序。
- 注意:“Singlnton instance”相当于分配内存对象空间;“new Singlnton()”相当于初始化对象;“=”相当于设置instance执行刚才分配的内存地址。
(2)静态内部类
public class Singlnton{
private Singlnton(){
}
private static class SinglntonHolder{
private static Singlnton INSTANCE = new Singlnton();
}
public static Singlnton getInstance(){
return SinglntonHolder.INSTANCE;
}
}
(3) Kotlin中的单例模式
class Singlnton{
companion object{
val instance : Singlnton by lazy { Singlnton() }
}
}
- lateinit和by lazy的区别:
- lateinit只能用于修饰变量var,不能用于可空的属性和Java的基本类型。
- lateinit可以在任何位置初始化并且可以初始化多次。
- lazy()只能用于修饰常量val,并且lazy()是线程安全的。
- lazy()是一个函数,可以接受一个Lambda表达式作为参数,第一次调用时会执行Lambda表达式,以后调用该属性会返回之前的结果。
- lazy()源码分析
3、优缺点
- 优点:(1)在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。(2)避免对资源的多重占用。
- 缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。