آیا متغیرهای Lazy کاتلین ترد سیف هستند؟
public enum class LazyThreadSafetyMode {
SYNCHRONIZED,
PUBLICATION,
NONE,
}
PUBLICATION: متد مقداردهنده در شرایط همزمانی بین ترد ها ممکن است چندین مرتبه صدا شود. اما اولین مقداری که برگردانده شود بعنوان مقدار اصلی آن استفاده میشود.
NONE: هیچ محدودیتی برای سینکرونایز کردن متد مقداردهنده آن انجام نشده و رفتار آن در شرایط همزمانی مشخص نیست. تنها زمانی قابل استفادهاست که مطمئن باشیم تنها روی یک ترد مقداردهی خواهد شد.
همچنین نیاز به گفتن نیست که خود Lazy یک اینترفیس با یک متد و یک متغیر که و مقدار متغیر رو نگه میداره:
public interface Lazy<out T> {
public val value: T
public fun isInitialized(): Boolean
}
internal object UNINITIALIZED_VALUE
// internal to be called from lazy in JS
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)
}
internal class InitializedLazyImpl<out T>(override val value: T) : Lazy<T>, Serializable {
override fun isInitialized(): Boolean = true
override fun toString(): String = value.toString()
}
نکته جالب این پیادهسازی وجود متد writeReplace که خروجی Any داره و private هم هست و هیچ جایی صدا زده نشده! پس کاربردش چیه؟
دلیلش توی Serializable وجود داره که توی لینک منبع ۲ توضیح داده شده.
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)
}
و دیگری اینکه وقتی ما متغیری رو بصورت زیر تعریف میکنیم، از همین پیادهسازی Lazy استفاده میکنیم
که یعنی ترد سیفه و با خیال راحت میتونیم تو شرایط همزمانیم ازش استفاده کنیم
private val lazyVariable by lazy { /* ... */ }
// uses
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
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
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()
if (valueUpdater.compareAndSet(this, UNINITIALIZED_VALUE, newValue)) {
initializer = null
return newValue
}
}
@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"
)
}
}
private val lazyVariable by lazy { /* ... */ }
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
private val lazyVariable by lazy(LazyThreadSafetyMode.PUBLICATION) { /* ... */ }
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)
}
private val LOCK = Any()
private val lazyVariable by lazy(LOCK) { /* ... */ }
public actual fun <T> lazy(lock: Any?, initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer, lock)