This story is the third part of the series, Exploring the lazy evaluation in Kotlin. If you didn’t read the previous ones you can start from below.

Kotlin By lazy Initializer

Lazy delegate simply creates an instance that performs initialization at the first access to the property value, stores the result, and return the stored value.

The lazy in Kotlin is a function which receives a lambda (the initialization block) and by is a keyword, as you can see the lazy function implementation below.

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) }

If we did not specify the LazyThreadSafetyMode, lazy implementation would be used SynchronizedLazyImp which performs the initialization only once by default.

Types of Lazy ThreadSafetyMode

There are three types of Lazy ThreadSafetyMode let’s see them one by one.

1. SYNCHRONIZED Mode

Locks will be used to ensure that the value will be executed in a single thread.

Other threads will be referred to as the cached value.

Do not try to synchronized from external code or it’ll cause an external deadlock.

Default mode for kotlin lazy initializer block.

Now let’s look at its implementation code:

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) }

The only noticeable thing in the above code is the execution of the initialization block with synchronized. It uses a lock to ensure that the initialization can only be done one time.

2. PUBLICATION Mode

The initializer block can be called on multiple threads concurrently.

When the value has been initialized by any thread, other threads will be used the previously cached value.

Recommended in a multi-threaded environment.

You can see the implementation of SafePublicationLazyImp on this link.

3. NONE Mode

Not recommended in a multi-threaded environment.

Note: In SYNCHRONIZED and PUBLICATION thread safety mode the by lazy captures the reference from the context where it is used. It will then store the references and release them only once the property has been initialized. This may lead to object hierarchies, such as Android activities, not being released for too long, so you should be careful about what you use inside the initializer lambda.

If the initialization of the value throws an exception in any ThreadSafetyMode, it will attempt to re-initialize at next access.

Practical Example Of Synchronized Vs Publication ThreadSafetyMode

import kotlinx.coroutines.Job import kotlinx.coroutines.* fun log(msg: String) = println("[${Thread.currentThread().name}] $msg") // change mode to PUBLICATION for multiple times initialization. val myLazilyEvaluatedValue: String by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { log("Initializing...") [email protected] "Ahsen Saeed initialized by -> ${Thread.currentThread().name}" } suspend fun main() { val jobs = mutableListOf<Job>() for(i in 0..3){ val job = kotlinx.coroutines.GlobalScope.launch{ println(myLazilyEvaluatedValue) } jobs.add(job) } jobs.joinAll() }

Now if you try to run the above code in SYNCHRONIZED mode the myLazilyEvaluatedValue only initialized once. But, if you change the mode to PUBLICATION and hit the run button three or four times, maybe you’ll be able to see that myLazilyEvaluatedValue initialized multiple times in a lazy block.

Alright, guys, this was my understanding about lazy evaluation with Lazy ThreadSafetyMode’s in Kotlin. Anything, I miss in this article please let me know via comments.

Thank you for being here and keep reading…