在实际开发中我们经常会用到lazy懒加载,比如说:
private val testManager by lazy {
TestManager()
}
private val testManager1 by lazy(LazyThreadSafetyMode.NONE) {
TestManager()
}
来看看lazy对应的实现方式:
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
/**
* Creates a new instance of the [Lazy] that uses the specified initialization function [initializer]
* and thread-safety [mode].
*
* If the initialization of a value throws an exception, it will attempt to reinitialize the value at next access.
*
* Note that when the [LazyThreadSafetyMode.SYNCHRONIZED] mode is specified the returned instance uses itself
* to synchronize on. Do not synchronize from external code on the returned instance as it may cause accidental deadlock.
* Also this behavior can be changed in the future.
*/
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)
}
/**
* Creates a new instance of the [Lazy] that uses the specified initialization function [initializer]
* and the default thread-safety mode [LazyThreadSafetyMode.SYNCHRONIZED].
*
* If the initialization of a value throws an exception, it will attempt to reinitialize the value at next access.
*
* The returned instance uses the specified [lock] object to synchronize on.
* When the [lock] is not specified the instance uses itself to synchronize on,
* in this case do not synchronize from external code on the returned instance as it may cause accidental deadlock.
* Also this behavior can be changed in the future.
*/
public actual fun <T> lazy(lock: Any?, initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer, lock)
通过代码可以知道lazy实际上有三种实现方式:
LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
Lazy接口如下,by lazy会委托到value上:
public interface Lazy<out T> {
/**
* Gets the lazily initialized value of the current Lazy instance.
* Once the value was initialized it must not change during the rest of lifetime of this Lazy instance.
*/
public val value: T
/**
* Returns `true` if a value for this Lazy instance has been already initialized, and `false` otherwise.
* Once this function has returned `true` it stays `true` for the rest of lifetime of this Lazy instance.
*/
public fun isInitialized(): Boolean
}
SYNCHRONIZED 也就是SynchronizedLazyImpl:
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
//如果value不等于默认值,则说明已经初始化,直接返回
if (_v1 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
return _v1 as T
}
//初始化过程加 synchronized锁
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)
}
当我们使用懒加载的testManager对象时,实际上是调用了Lazy.value,即会走上面的get方法,初始化过程会通过synchronized来加锁,所以它是线程安全的。synchronized经过不断地优化,一般情况下是轻量级的锁了。但在锁竞争激烈,锁持有时间长的时候(也就是同时有多个线程使用这个testManager实例且初始化又比较耗时),会升级到重量级锁,损耗性能。如果这个锁被某个子线程获取了同时初始化方法又比较耗时,主线程需要使用lazy对象的话,就会陷入等待锁的过程。
上面在加锁后再进行了一次判断,这就很类似于Java里面的双重检查的单例模式,另外_value用关键字Volatile修饰也一样。
PUBLICATION
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
//如果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比较_value,如果等于UNINITIALIZED_VALUE,则赋值为newValue
if (valueUpdater.compareAndSet(this, UNINITIALIZED_VALUE, newValue)) {
initializer = null
return newValue
}
}
//初始化函数为空,或者compareAndSet返回flase,说明已经赋值好了,直接返回
@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.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater(
SafePublicationLazyImpl::class.java,
Any::class.java,
"_value"
)
}
}
可以看出initializerValue()没有同步机制,初始化方法可能会执行多次。
这个方法会以原子操作去更新指定对象的属性值,通过CAS方式去判断_value是否为UNINITIALZED_VALUE值,如果是则将其更新为newValue并返回true,否则不操作(说明已经被更新了)返回false。这个也是线程安全的。
NONE
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)
}
这种方式最简单,就是在get的时候判断一下是否已经初始化,是则直接返回,否则初始化再返回。
没有任何线程安全的处理,所以它是线程不安全的,多线程调用下可能会初始化多次,导致逻辑异常。
总结: SYNCHRONIZED
线程安全,整个初始化过程被synchronized包含,所以多线程下初始化函数不会执行多次,但首次获取到锁的线程可能会阻塞其他线程(初始化时间可能长,对于主线程也要使用这个属性的场景,需要额外注意)。一般情况下synchronized比较轻量,可以放心使用,但在锁竞争激烈,锁持有时间长的时候,会升级到重量级锁,会经历用户态和内核态的切换,损耗性能。
PUBLICATION
线程安全,多线程下初始化函数可能会执行多次,但只要第一个初始化结果会被实际赋值,不影响使用,初始化函数不会阻塞其他线程,只有在赋值时才使用CAS机制。这种方式虽然避免了synchronized同步,但也增加了额外的工作量(初始化函数执行多次)。但Kotlin提供了这机制,我们需要在某些场景可以去权衡具体该使用谁,比较synchronized有膨胀的风险。
NONE
非线程安全 多线程调用下可能会初始化多次,导致逻辑异常,没有并发场景时,性能最好。