擁抱kotlin:利用kotlin實(shí)現(xiàn)單例模式

java的單例模式幾種寫(xiě)法都已經(jīng)很熟悉了,但轉(zhuǎn)到kt時(shí)如果用java寫(xiě)法實(shí)現(xiàn)倒顯得怪異了,有的可以借助kt的約定輕松完成。

一、幾種常見(jiàn)的單例模式

  1. 餓漢式(一來(lái)就創(chuàng)建,不管是否真的需要)
  2. 懶漢式(延遲加載,使用時(shí)才創(chuàng)建,這里就直接討論double check)
  3. 靜態(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

image

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)的地方加了注釋

  1. _value用volatile禁止指令重排序
  2. 這個(gè)是上面分析的單例委托的value
  3. 若已經(jīng)初始化,則直接返回 同java中的第一層判斷
  4. 第二層判斷,加synchronized關(guān)鍵幀 保證線程安全
  5. 第一次創(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í)候才加載,保證了延遲加載。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容