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
}
}
}
//....
}
上面就是最为核心的代码了
get
方法的首先判定是_value
是否已经赋值了,如果赋值了直接返回。因为lazy
只会被延迟加载一次,所以将最有可能的情况,放在了最上面判定。v1
没有赋值的时候,进入加锁和赋值判定的流程了。在某个线程获取到锁的时候,还在锁内部判定了一次是否value
有被赋值,避免了高并发下的2次初始化化的情况。这篇文章本来是想自己写一个Java
版本的lazy
的,结果写的时候,研究源码,就发现,自己写也无非是这样,所以就还是稍微解读下其源码吧...
Java
中Spring
和Guava
都有提供类似的实现,有兴趣的可以看看他们对应的实现。