Kotlin关键字by lazy中的Mode分析

    class TestManager private constructor(){
        companion object{
            val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED){
                TestManager()
            }
        }
    }

上面代码是常见的Kotlin中通过by关键字来创建单例对象的实现,但lazy中的Mode是什么呢?
了解这些之前,我们先简单分别说下lazy和by,其中by是Kotlin中用于委托的关键字,而lazy是一个方法,其返回值是委托的具体对象。by lazy用于实现数据的惰性加载,在初始化一个常量时确保其不会被多次初始化。
LazyThreadSafetyMode的值有三个,通过它来确定对象的创建模式:

  • LazyThreadSafetyMode.SYNCHRONIZED
  • LazyThreadSafetyMode.PUBLICATION
  • LazyThreadSafetyMode.NONE

lazy方法默认是使用SynchronizedLazyImpl来返回委托的具体对象的。
Lazy实现了by lazy的方法:

@kotlin.internal.InlineOnly
public inline operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value

也就是我们的获取到的值就是这个value,那么我们就要来看这个value是怎么获取到的。分析三种Mode下value不同取值,可以确定后续我们在使用by lazy时应该使用那种Mode,首先先看SynchronizedLazyImpl的实现:

private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
    //lazy后的方法体,返回当前需要创建的对象
    private var initializer: (() -> T)? = initializer
    //默认值,定义了一个未初始化的值,为了解决DCL带来指令重排序导致主存和工作内存数据不一致的问题,这里使用Volatile原语注解,线程安全
    @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
        //获取value的值就是通过get方法
        get() {
            val _v1 = _value
            //如果当前的值已经不是未初始化的了那么直接返回
            if (_v1 !== UNINITIALIZED_VALUE) {
                @Suppress("UNCHECKED_CAST")
                return _v1 as T
            }

            return synchronized(lock) {
                //双重检测
                val _v2 = _value
                if (_v2 !== UNINITIALIZED_VALUE) {
                    @Suppress("UNCHECKED_CAST") (_v2 as T)
                } else {
                    //如果没有初始化,那么通过initializer来进行初始化操作
                    val typedValue = initializer!!()
                    //并将这个值保存在_value中
                    _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)
}

SafePublicationLazyImpl的实现:

private class SafePublicationLazyImpl<out T>(initializer: () -> T) : Lazy<T>, Serializable {
    @Volatile private var initializer: (() -> T)? = initializer
    @Volatile private var _value: Any? = UNINITIALIZED_VALUE
    // this final field is required to enable safe initialization of the constructed instance
    private val final: Any = UNINITIALIZED_VALUE

    override val value: T
        get() {
            val value = _value
            if (value !== UNINITIALIZED_VALUE) {
                @Suppress("UNCHECKED_CAST")
                return value as T
            }
            val initializerValue = initializer
            // if we see null in initializer here, it means that the value is already set by another thread
            if (initializerValue != null) {
                   //如果是多线程的话,初始化方法会调用多次
                val newValue = initializerValue()
                //通过CAS保证只有在原始值为UNINITIALIZED_VALUE时赋值
                if (valueUpdater.compareAndSet(this, UNINITIALIZED_VALUE, newValue)) {
                    initializer = null
                    return newValue
                }
            }
            @Suppress("UNCHECKED_CAST")
            return _value as T
        }

    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)

    companion object {
        private val valueUpdater = 
        //java中的原子操作
        java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater(
            SafePublicationLazyImpl::class.java,
            Any::class.java,
            "_value"
        )
    }
}

UnsafeLazyImpl的实现:

internal class UnsafeLazyImpl<out T>(initializer: () -> T) : Lazy<T>, Serializable {
    private var initializer: (() -> T)? = initializer
    private var _value: Any? = UNINITIALIZED_VALUE

    override val value: T
        get() {
            //只是是未初始化那么就调用初始化方法
            //多线程的情况下,那么它的值为最后的赋值方法,线程不安全
            if (_value === UNINITIALIZED_VALUE) {
                _value = initializer!!()
                initializer = null
            }
            @Suppress("UNCHECKED_CAST")
            return _value as T
        }

    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)
}

经过上面的源码分析,我们不难看出三种Mode的区别:

  • LazyThreadSafetyMode.SYNCHRONIZED 通过synchronized关键值创建同步锁来实现线程安全
  • LazyThreadSafetyMode.PUBLICATION 通过CAS来保证线程安全,多线程的情况下初始化方法会调用,但只有一个生效。
  • LazyThreadSafetyMode.NONE,只要标识为未初始化就调用初始化来赋值,线程不安全。

所以经过上面的分析,在确定当前属性不会在多个线程之间共享的时候,则可以使用NONE模式,这是最快的模式,但也是最不安全的模式。
如果属性是要在多个线程间共享,那么使用SYNCHRONIZED和PUBLICATION,但因为SYNCHRONIZED引入了锁的机制,它是最安全的,但也是最慢的模式。折中的话则可以使用PUBLICATION。