Kotlin 標準庫為幾種有用的委托提供了工廠方法。
- 延遲屬性(lazy properties): 其值只在首次訪問時計算桶唐;
- 可觀察屬性(observable properties): 監(jiān)聽器會收到有關此屬性變更的通知;
- 把多個屬性儲存在一個映射(map)中综芥,而不是每個存在單獨的字段中力崇。
延遲屬性 Lazy
lazy()
是接受一個 lambda
并返回一個 Lazy <T>
實例的函數(shù),返回的實例可以作為實現(xiàn)延遲屬性的委托.
val lazyValue: String by lazy {
println("set lazyValue")
"lazyInit"
}
fun main() {
println(lazyValue)
println(lazyValue)
}
// set lazyValue
// lazyInit
// lazyInit
從示例中可以看出,
當?shù)谝淮问褂玫皆撝禃r,會調用
lambda
函數(shù),并且將函數(shù)返回值,賦給該變量. 后續(xù)則直接返回該值,而不再調用lambda
函數(shù).
修飾延遲屬性只能使用val
,說明延遲屬性只能修飾不變的變量,它沒有對應的set函數(shù),只有get函數(shù).
kotlin角度分析Lazy
點開lazy
方法,跳轉到LazyJVM.kt
文件.
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
// 已經(jīng)初始化的時候,直接返回 初始化后的值
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 {
// 通過傳入的函數(shù),得到函數(shù)返回值
val typedValue = initializer!!()
// 將返回值賦值給_value
_value = typedValue
// 置空函數(shù),輔助GC
initializer = null
typedValue
}
}
}
...
}
lazy
最后調用的是SynchronizedLazyImpl
類中的相關方法.
當訪問字段變量的時候,最終調用就是對應的get
方法.
當_value
有值時,則直接返回, 當_value
沒有值時,通過調用傳入的lambda
函數(shù),得到lambda
函數(shù)的返回值作為_value
的值返回.
由此,實現(xiàn)懶加載的功能.
可以看出, 默認該方式是使用同步鎖進行的,所以它是線程安全的.
即該值只在一個線程中計算欣舵,并且所有線程會看到相同的值。
lazy的三種模式
public enum class LazyThreadSafetyMode {
/**
* Locks are used to ensure that only a single thread can initialize the [Lazy] instance.
*/
SYNCHRONIZED,
/**
* Initializer function can be called several times on concurrent access to uninitialized [Lazy] instance value,
* but only the first returned value will be used as the value of [Lazy] instance.
*/
PUBLICATION,
/**
* No locks are used to synchronize an access to the [Lazy] instance value; if the instance is accessed from multiple threads, its behavior is undefined.
*
* This mode should not be used unless the [Lazy] instance is guaranteed never to be initialized from more than one thread.
*/
NONE,
}
lazy
函數(shù)主要有3中模式
- SYNCHRONIZED 同步方式
延遲屬性的值,只允許一個線程中修改,其他線程只能使用該線程修改的值.
- PUBLICATION 公共方式
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) {
// 初始化的lambda函數(shù)可被多個線程執(zhí)行
val newValue = initializerValue()
// 通過CAS操作,只有一個線程,能夠修改_value值
if (valueUpdater.compareAndSet(this, UNINITIALIZED_VALUE, newValue)) {
initializer = null
return newValue
}
}
@Suppress("UNCHECKED_CAST")
return _value as T
}
允許多個線程在同一時刻進入初始化階段,但是只有一個線程可以修改成功.
- NONE 模式
get() {
if (_value === UNINITIALIZED_VALUE) {
_value = initializer!!()
initializer = null
}
@Suppress("UNCHECKED_CAST")
return _value as T
}
線程不安全.
模式使用
val lazyValue: String by lazy(LazyThreadSafetyMode.PUBLICATION) {
println("set lazyValue")
"lazyInit"
}