驽马十驾 驽马十驾

驽马十驾,功在不舍

目录
kotlin 的lazy实现源码解读
/  

kotlin 的lazy实现源码解读

开篇

kotlin中有一个非常有意思的关键字lazy,表示该变量会在被使用的时候,才会加载,我思考下了,这样可以代码如下几个好处。

  • 避免对象提前占用了内存资源
  • 避免拖慢项目启动速度

解读

class KtLazyThinking {
    
    private val name: String by lazy {
        println("invole...")
        "hello"
    }

    @Test
    fun lazy1() {
        println(name)
    }
}

用法很简单,只需要通过by lazy {...}修饰即可。

好奇了下,通过反编译,查看了下其实现:

   public KtLazyThinking() {
      this.name$delegate = LazyKt.lazy((Function0)null.INSTANCE);
   }

核心是LazyKt下的lazy方法

public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)

再继续往里面找

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
  
    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
                }
            }
        }
	//....
}

上面就是最为核心的代码了

  • 细节1:get方法的首先判定是_value是否已经赋值了,如果赋值了直接返回。因为lazy只会被延迟加载一次,所以将最有可能的情况,放在了最上面判定。
  • 细节2:当发现v1没有赋值的时候,进入加锁和赋值判定的流程了。在某个线程获取到锁的时候,还在锁内部判定了一次是否value有被赋值,避免了高并发下的2次初始化化的情况。

结语

这篇文章本来是想自己写一个Java版本的lazy的,结果写的时候,研究源码,就发现,自己写也无非是这样,所以就还是稍微解读下其源码吧...

JavaSpringGuava都有提供类似的实现,有兴趣的可以看看他们对应的实现。

积土成山,风雨兴焉。积水成渊,蛟龙生焉。