- 饿汉模式
- 懒汉模式
- 线程安全的懒汉模式
- 双重校验锁的懒汉模式
- 静态内部类的懒汉模式
单例模式是开发中最常用的设计模式,Kotlin
中实现单例模式是很简单的,下面对比下 Kotlin
和 Java
单例模式实现。
一、饿汉模式
- Java -饿汉模式
// 单例-饿汉模式
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() { }
public static Singleton get() { return instance; }
}
- Kotlin -饿汉模式
// 单例-饿汉模式
object Singleton
Kotlin 中实现 饿汉模式 的单例是非常简单的,通过关键字 object
+ 类名
即可实现。
- Java -Kotlin 转换 Java
为了验证 object
+ 类名
即可实现 kotlin 版本的单例模式,我们通过 Android studio
编译器将 Kotlin 代码转换为 Java 代码,如下:
public final class Singleton {
public static final Singleton INSTANCE;
private Singleton() { }
// 在类加载时执行
static {
Singleton var0 = new Singleton();
INSTANCE = var0;
}
}
从转换后的代码中可以看出,定义的只读静态成员变量 INSTANCE
是在静态代码块中初始化,也就是说在类加载时已经对成员变量进行了初始化,同时构造方法为私有满足 饿汉模式 的单例特点,因此 object
+ 类名
实际是一个 饿汉模式 的单例。
二、懒汉模式
- Java -懒汉模式
// 单例-懒汉模式
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() { }
public static Singleton get() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
- Kotlin -懒汉模式
// 单例-懒汉模式
class Singleton private constructor() {
companion object {
private var instance: Singleton? = null
get() {
if (field == null) {
field = Singleton()
}
return field
}
@JvmStatic
fun get(): Singleton = instance!!
}
}
- Java -Kotlin 转换 Java
public final class Singleton {
private static Singleton instance; // ①
public static final Singleton.Companion Companion = new Singleton.Companion((DefaultConstructorMarker)null); // ③
private Singleton() {
}
// $FF: synthetic method
public Singleton(DefaultConstructorMarker $constructor_marker) {
this();
}
@JvmStatic
@NotNull
public static final Singleton get() { // ②
return Companion.get();
}
public static final class Companion {
private final Singleton getInstance() { // ⑤
if (Singleton.instance == null) {
Singleton.instance = new Singleton((DefaultConstructorMarker)null);
}
return Singleton.instance;
}
private final void setInstance(Singleton var1) {
Singleton.instance = var1;
}
@JvmStatic
@NotNull
public final Singleton get() { // ④
Singleton var10000 = ((Singleton.Companion)this).getInstance();
if (var10000 == null) {
Intrinsics.throwNpe();
}
return var10000;
}
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
代码第②行构建了一个 Companion
对象的静态成员变量,在 Companion
对象中提供了 get()
方法获得 Singleton
实例,而在此 get()
方法中实际是调用代码⑤真正的创建 Singleton
实例,同时将此实例赋值给代码代码①的静态成员变量。从代码实现上看与我们上面的 Java 实现是一致的,所以上面的 kotlin 实现是 懒汉模式 的单例。
三、线程安全的懒汉模式
- Java -线程安全的懒汉模式
// 单例-线程安全的懒汉模式
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() { }
public static Singleton get() {
synchronized (Singleton.class) {
if (instance == null)
instance = new Singleton();
}
return instance;
}
}
- Kotlin -线程安全的懒汉模式
// 单例-线程安全的懒汉模式
class Singleton private constructor() {
companion object {
private var instance: Singleton? = null
get() {
if (field == null) {
field = Singleton()
}
return field
}
@JvmStatic
@Synchronized
fun get(): Singleton = instance!!
}
}
- Java -Kotlin 转换 Java
public final class Singleton {
private static Singleton instance;
public static final Singleton.Companion Companion = new Singleton.Companion((DefaultConstructorMarker)null);
private Singleton() {
}
// $FF: synthetic method
public Singleton(DefaultConstructorMarker $constructor_marker) {
this();
}
@JvmStatic
@NotNull
public static final synchronized Singleton get() { // 1️⃣
return Companion.get();
}
public static final class Companion {
private final Singleton getInstance() {
if (Singleton.instance == null) {
Singleton.instance = new Singleton((DefaultConstructorMarker)null);
}
return Singleton.instance;
}
private final void setInstance(Singleton var1) {
Singleton.instance = var1;
}
@JvmStatic
@NotNull
public final synchronized Singleton get() { // 2️⃣
Singleton var10000 = ((Singleton.Companion)this).getInstance();
if (var10000 == null) {
Intrinsics.throwNpe();
}
return var10000;
}
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
从代码可以看出,线程安全的懒汉模式转后的代码与懒汉模式转后的代码基本一致,只是比懒汉模式在代码1️⃣2️⃣中多出一个 synchronized
关键字修饰方法,保证方法在同一时间只有一个线程在访问。与我们实现 Java 版本的线程安全的懒汉模式是一样的,间接的验证了 Kotlin 代码实现的正确性。
四、双重校验锁的懒汉模式
- Java -双重校验锁的懒汉模式
// 单例-双重校验锁的懒汉模式
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() { }
public static Singleton get() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
- Kotlin -双重校验锁的懒汉模式
// 单例-双重校验锁的懒汉模式
class Singleton private constructor() {
companion object {
val instance: Singleton by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { Singleton() }
}
}
kotlin 实现双重校验锁单例主要是通过关键字 lazy
实现的,所以此处应该看下 SYNCHRONIZED
模式下的 lazy
实现。
public actual fun lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy =
when (mode) {
LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
}
SYNCHRONIZED
模式实际调用的是 SynchronizedLazyImpl(initializer)
,来看下SynchronizedLazyImpl
的实现:
private class SynchronizedLazyImpl(initializer: () -> T, lock: Any? = null) : Lazy, Serializable {
private var initializer: (() -> T)? = initializer
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
// final field is required to enable safe publication of constructed instance
private val lock = lock ?: this
override val value: T
get() {
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) { // 1️⃣
@Suppress("UNCHECKED_CAST")
return _v1 as T
}
return synchronized(lock) { // 2️⃣
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) { // 3️⃣
@Suppress("UNCHECKED_CAST") (_v2 as T)
} else {
val typedValue = initializer!!()
_value = typedValue
initializer = null
typedValue
}
}
}
override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
private fun writeReplace(): Any = InitializedLazyImpl(value)
}
源码实现可以看出,SynchronizedLazyImpl
的核心代码是属性 value
的 get
访问器的实现。在 get
访问器中实现了双重校验锁的功能,代码第1️⃣3️⃣对应双重效验,代码第2️⃣进行加锁效验,防止 initializer
的多次调用。
五、静态内部类的懒汉模式
- Java -静态内部类的懒汉模式
// 单例-静态内部类的懒汉模式
public class Singleton {
private Singleton() { }
public static Singleton get() {
return Holder.INSTANCE;
}
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
}
- Kotlin -静态内部类的懒汉模式
// 单例-静态内部类的懒汉模式
class Singleton private constructor() {
companion object {
@JvmStatic
fun get(): Singleton = Holder.INSTANCE
}
private object Holder {
val INSTANCE = Singleton()
}
}
- Java -Kotlin 转换 Java
public final class Singleton {
public static final Singleton.Companion Companion = new Singleton.Companion((DefaultConstructorMarker)null);
private Singleton() {
}
// $FF: synthetic method
public Singleton(DefaultConstructorMarker $constructor_marker) {
this();
}
@JvmStatic
@NotNull
public static final Singleton get() {
return Companion.get();
}
private static final class Holder {
@NotNull
private static final Singleton INSTANCE;
public static final Singleton.Holder INSTANCE;
@NotNull
public final Singleton getINSTANCE() {
return INSTANCE;
}
static {
Singleton.Holder var0 = new Singleton.Holder();
INSTANCE = var0;
INSTANCE = new Singleton((DefaultConstructorMarker)null);
}
}
public static final class Companion {
@JvmStatic
@NotNull
public final Singleton get() {
return Singleton.Holder.INSTANCE.getINSTANCE();
}
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
上述代码可以看出 Holder
对象是 Singleton
的静态内部类,同时在 Holder
的静态代码块中构建了 Singleton
的静态对象,因此只有在 Singleton
对象中提供一个能访问 Holder
对象中 INSTANCE
方法既可以实现静态内部类的单例,Holder
对象中提供了方法 getINSTANCE()
访问成员 INSTANCE
,而 Companion
对象的 get
方法是调用 Holder
的 getINSTANCE()
方法返回 Singleton
对象,最后通过 Singleton
对象本身 get()
调用 Companion.get()
访问 Singleton
实例。