Kotlin之基于lazy源码分析lazy原理

2,095 阅读2分钟

lazy() 是接受一个 lambda 并返回一个 Lazy <T> 实例的函数,返回的实例可以作为实现延迟属性的委托: 第一次调用 get() 会执行已传递给 lazy() 的 lambda 表达式并记录结果, 后续调用 get() 只是返回记录的结果。

val lazyValue: String by lazy {

println("computed!")

"Hello"

}

fun main() {

println(lazyValue)

println(lazyValue)

}

运行结果为:

computed!

Hello

Hello

默认情况下,对于 lazy 属性的求值是同步锁的(synchronized):该值只在一个线程中计算,并且所有线程会看到相同的值。如果初始化委托的同步锁不是必需的,这样多个线程可以同时执行,那么将 LazyThreadSafetyMode.PUBLICATION 作为参数传递给 lazy() 函数。而如果你确定初始化将总是发生在与属性使用位于相同的线程, 那么可以使用 LazyThreadSafetyMode.NONE 模式:它不会有任何线程安全的保证以及相关的开销。

lazy函数对应的源码是LazyJVM.kt,val lazyValue: String by lazy {}调用的是下面黄色高亮的构造方法,它使用的是SynchronizedLazyImpl。lazy后面的{}其实是一个Lambda表达式,它会传递给initializer,而initializer又会作为SynchronizedLazyImpl的构造参数。

SynchronizedLazyImpl的成员变量 _value 的初始值是UNINITIALIZED_VALUE,如果 _value的值不是UNINITIALIZED_VALUE,说明已经初始化赋值过了,于是就直接返回 _value;

如果 _value的值是UNINITIALIZED_VALUE,说明还没有初始化赋值,就在synchronized(lock) 同步块中进行赋值,这里还采用了double check的方式,再次确定 _value没有被赋值,于是就通过Lambda表达式的返回值typedValue作为为初始值并返回。

对于使用了LazyThreadSafetyMode.PUBLICATION这种模式的lazy方法,它将使用不同的XxxImpl,比如SafePublicationLazyImpl和UnsafeLazyImpl,它们没有同步,性能高,但不保证多线程安全性。

public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
    when (mode) {
        LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
        LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
        LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
    }

public actual fun <T> lazy(lock: Any?, initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer, lock)



private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, 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) {
                @Suppress("UNCHECKED_CAST")
                return _v1 as T
            }

            return synchronized(lock) {
                val _v2 = _value
                if (_v2 !== UNINITIALIZED_VALUE) {
                    @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)
}