java的單例模式幾種寫(xiě)法都已經(jīng)很熟悉了,但轉(zhuǎn)到kt時(shí)如果用java寫(xiě)法實(shí)現(xiàn)倒顯得怪異了,有的可以借助kt的約定輕松完成。
一、幾種常見(jiàn)的單例模式
- 餓漢式(一來(lái)就創(chuàng)建,不管是否真的需要)
- 懶漢式(延遲加載,使用時(shí)才創(chuàng)建,這里就直接討論double check)
- 靜態(tài)內(nèi)部類(lèi)(借助虛擬機(jī)實(shí)現(xiàn)同步、內(nèi)部類(lèi)機(jī)制實(shí)現(xiàn)延遲初始化)
二、kotlin這幾種方式的寫(xiě)法
1.餓漢式
這種方式無(wú)需使用java的private constroctor,getInstance這些接口,直接使用object就可實(shí)現(xiàn)。
object HungrySin {
fun calculate() {
}
}
反編譯成java代碼,就是熟悉的java寫(xiě)法
public final class HungrySin {
public static final HungrySin INSTANCE;
public final void calculate() {
}
private HungrySin() {
}
static {
HungrySin var0 = new HungrySin();
INSTANCE = var0;
}
}
2.懶漢式 doubleCheck
說(shuō)到延遲加載就直接跳到線程安全且性能較好的doubleCheck吧,若是用java實(shí)現(xiàn)會(huì)用2層check,第一層判斷減輕鎖的負(fù)擔(dān)直接判斷是否創(chuàng)建過(guò),第二層判斷加鎖保證線程安全,最后用volatile禁止重排序防止編譯器優(yōu)化導(dǎo)致的線程安全問(wèn)題。
在kotlin里面也無(wú)需這么復(fù)雜,直接使用by lazy代理即可實(shí)現(xiàn)
class DoubleCheckSin private constructor() {
companion object {
val doubleCheckSin: DoubleCheckSin by lazy {
DoubleCheckSin()
}
}
fun calculate() {
}
}
這個(gè)要?dú)w功于委托和lazy.kt
@kotlin.internal.InlineOnly
public inline operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value
可以看到上文,把val doubleCheckSin委托給了lazy, get時(shí)返回的是lazy.kt的value
重點(diǎn)來(lái)看看lazy
layz支持傳LazyThreadSafetyMode,有三種可選,默認(rèn)的是SYNCHRONIZED,也就是線程安全,下面分析一下線程安全的具體實(shí)現(xiàn)。
private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
private var initializer: (() -> T)? = initializer
//0._value用volatile禁止指令重排序
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
// final field is required to enable safe publication of constructed instance
private val lock = lock ?: this
//1.這個(gè)是上面分析的單例委托的value
override val value: T
get() {
val _v1 = _value
//2.若已經(jīng)初始化,則直接返回 同java中的第一層判斷
if (_v1 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
return _v1 as T
}
return synchronized(lock) {
val _v2 = _value
//3.第二層判斷,加synchronized關(guān)鍵幀 保證線程安全
if (_v2 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST") (_v2 as T)
} else {
//4.第一次創(chuàng)建,把執(zhí)行l(wèi)azy下傳進(jìn)來(lái)的lambda 單例這里也就是值的構(gòu)造構(gòu)造函數(shù)
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)
}
上面的代碼對(duì)應(yīng)的地方加了注釋
- _value用volatile禁止指令重排序
- 這個(gè)是上面分析的單例委托的value
- 若已經(jīng)初始化,則直接返回 同java中的第一層判斷
- 第二層判斷,加synchronized關(guān)鍵幀 保證線程安全
- 第一次創(chuàng)建,把執(zhí)行l(wèi)azy下傳進(jìn)來(lái)的lambda 單例這里也就是值的構(gòu)造構(gòu)造函數(shù)
由此可以看出lazy的SYNCHRONIZED模式實(shí)現(xiàn)和java是完全一樣的(除了委托和lambda函數(shù)),但是使用koltin by lazy實(shí)現(xiàn)doubelCheck單例就就可以少寫(xiě)很多代碼
對(duì)于by layz其實(shí)用的更多的地方是view的延遲加載,道理和單例一樣的。第一次訪問(wèn)的時(shí)候才執(zhí)行l(wèi)ambda來(lái)初始化,后面就直接取之前創(chuàng)建的對(duì)象。
3.靜態(tài)內(nèi)部類(lèi)
這個(gè)其實(shí)沒(méi)有取巧的地方了,還是得像java一樣要聲明一個(gè)靜態(tài)內(nèi)部類(lèi),通過(guò)虛擬機(jī)的靜態(tài)內(nèi)部類(lèi)加載機(jī)制實(shí)現(xiàn)線程安全、延遲加載。
class InnerSin private constructor() {
companion object {
val instance = Holder.holder
}
private object Holder {
val holder = InnerSin()
}
fun calculate() {
}
}
不過(guò)這種方式很巧妙,利用加載類(lèi)的cinit方法特行,保證加載靜態(tài)類(lèi)holder時(shí)創(chuàng)建InnerSin對(duì)象是線程安全且只會(huì)有一個(gè)實(shí)例。然后加載外部類(lèi)加載不會(huì)馬上加載內(nèi)部類(lèi),等到外部類(lèi)調(diào)用內(nèi)部類(lèi)的時(shí)候才加載,保證了延遲加載。