单例模式
定义:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
通常我们可以通过一个全局变量使得一个对象可以被访问,但它不能防止被实例化多个对象。一个最好的解决方式是,
让类自身负责保存它的唯一实例
。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。
常用的几种写法
懒汉式单例模式
-
定义:在第一次被引用的时,才会将自己实例化。
-
参考代码:
/**
* @create on 2020/6/14 12:10
* @description 懒汉式 单例模式
* @author mrdonkey
*/
class LazySingleton {
private constructor() //私有构造,防止外界实例化它
companion object {
private var instance: LazySingleton? = null
@JvmStatic
fun getInstance(): LazySingleton {//获取唯一实例的入口
if (instance == null) {//如果为空,则新建一个实例返回,反之返回它
instance = LazySingleton()
}
return instance as LazySingleton
}
}
}
好处:
通过SingleTon模式封装类的实例,可以对唯一实例的进行受控访问。
多线程时的单例
在多线程的程序中,多个线程同时访问获取类实例的getInstance
方法时,可能造成的创建多个实例。
懒汉式加锁的单例模式
-
简单概览:
给进入该方法体的当前进程加一把锁🔐,在该进程调用该方法的过程中,其他进程若试图进入该方法则将会等待,直到前面的进程执行完,锁释放后才进会方法体。 -
基本代码:
/**
* @create on 2020/6/14 12:45
* @description 懒汉式加锁单例模式
* @author mrdonkey
*/
class LazyLockSingleton {
private constructor()
companion object {
private var instance: LazyLockSingleton? = null
@JvmStatic
fun getInstance(): LazyLockSingleton {
synchronized(this) {
if (instance == null) {
instance = LazyLockSingleton()
}
}
return instance as LazyLockSingleton
}
}
}
不足:
进程每次进入方法都会被上锁,这样会有些性能损耗,可以先判断当前实例是否为空再去加锁,下面的双重检锁
的写法就是解决这个不足。
懒汉式双重检锁的单例模式
双重检锁(Double-Check-Locking):
为了不让线程每次都加锁,而只是在实例未创建的时候才去加锁,保证线程安全。基本代码:
/**
* @create on 2020/6/14 12:17
* @description 双重检索、饿汉式 单例模式 针对多线程的情况 保证线程安全
* @author mrdonkey
*/
class LazyDCLSingleton {
private constructor()
companion object {
private var instance: LazyDCLSingleton? = null
@JvmStatic
fun getInstance(): LazyDCLSingleton {
if (instance == null) {//第一重
synchronized(this) {
if (instance == null) {//第二重
instance = LazyDCLSingleton()
}
}
}
return instance as LazyDCLSingleton
}
}
}
疑问:
为什么需要两次实例为空的判断呢?
因为,多线程中,第一个线程先进入该方法并加锁处理已经进入第二重,第二个线程此时是可以进入第一重为空的判断里,等到第一个线程创建了一个实例,第二个线程才可以进入,若不再加一重为空的判断,那么第二个线程也会重新创建一个实例,导致第一个实例被覆盖掉了!
饿汉式单例模式
-
定义:在类被加载时就去实例化自己。
-
基本代码:
/**
* @create on 2020/6/14 12:09
* @description 饿汉式 单例模式
* @author mrdonkey
*/
class HungrySingleton {
private constructor()
companion object {
private val instance: HungrySingleton = HungrySingleton()//val 引用不可更改,类加载时就去实例化,因为一般来说类之后加载一次。
@JvmStatic
fun getInstance(): HungrySingleton {
return instance
}
}
-
优势:
相比懒汉式单例模式,它是线程安全的单例模式,不需要做加锁处理。 -
不足:
提前占用系统资源
枚举类单例
/**
* @create on 2020/6/14 13:00
* @description 枚举类定义的对象都是单例的,无偿提供序列化支持。
* @author mrdonkey
*/
enum class EnumSingleton {
INSTANCE
}