在阎宏博士的《JAVA与模式》一书中开头是这样描述单例模式的:
作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。
单例模式的特点:
- 单例类只能有一个实例
- 单例类必须自已创建自已唯一的实例
- 单例类必须给所有其它对象提供这一实例
饿汉单例类, 饿汉式是典型的空间换时间,当类装载的时候就会创建类的实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断,节省了运行时间。
//饿汉式kotlin实现 object EagerSingleTon //等于如下java代码 /** * public static final class EagerSingleTon { public static final Main.EagerSingleTon INSTANCE; private EagerSingleTon() { } static { Main.EagerSingleTon var0 = new Main.EagerSingleTon(); INSTANCE = var0; } } * */
懒汉单例类,懒汉式其实是一种比较形象的称谓。既然懒,那么在创建对象实例的时候就不着急。会一直等到马上要使用对象实例的时候才会创建,懒人嘛,总是推脱不开的时候才会真正去执行工作,因此在装载对象的时候不创建对象实例。
class LazySingleton private constructor() { companion object { val instance: LazySingleton by lazy { LazySingleton() } } }
由于懒汉式的实现是线程安全的,这样会降低整个访问的速度,而且每次都要判断。
双重检查加锁
可以使用“双重检查加锁”的方式来实现,就可以既实现线程安全,又能够使性能不受很大的影响
class LazySingleton private constructor() { companion object { //双重锁实现 val instance: LazySingleton by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { LazySingleton() } } }
所谓“双重检查加锁”机制,指的是:并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法后,先检查实例是否存在,如果不存在才进行下面的同步块,这是第一重检查,进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。
另外,我们运用到了kotlin的延迟属性 Lazy:
Lazy是接受一个 lambda 并返回一个 Lazy 实例的函数,返回的实例可以作为实现延迟属性的委托: 第一次调用 get() 会执行已传递给 lazy() 的 lambda 表达式并记录结果, 后续调用 get() 只是返回记录的结果。