Kotlin delegate property by lazy that is thread local

1.5k views Asked by At

Is there a simple way get a delegated property by lazy's value computed per thread like ThreadLocal?

LazyThreadSafetyMode controls concurrent initialization, with .NONE coming close to the desired functionality by allowing multiple threads to receive different values, but has subsequent post initialization calls referencing the same object, returning the same singular value regardless of thread, with some cases returning null.

Regardless of concurrent initialization, or late initialization, the property would cache a unique value per thread.

1

There are 1 answers

0
hotkey On BEST ANSWER

The Kotlin delegates are easy to extend with your own implementation.

  • You can make your delegate maintain a ThreadLocal<T> with initialValue calculated by the function that is passed:

    class ThreadLocalLazy<T>(val provider: () -> T) :ReadOnlyProperty<Any?, T> {
        private val threadLocal = object : ThreadLocal<T>() {
            override fun initialValue(): T = provider()
        }
    
        override fun getValue(thisRef: Any?, property: KProperty<*>): T =
            threadLocal.get()
    }
    
  • Or maintain a Lazy<T> per thread with ThreadLocal<Lazy<T>>, so that your delegate can implement Lazy<T> by itself:

    class ThreadLocalLazy<T>(val provider: () -> T) : Lazy<T> {
        private val threadLocal = object : ThreadLocal<Lazy<T>>() {
            override fun initialValue(): Lazy<T> = 
                lazy(LazyThreadSafetyMode.NONE, provider)
        }
    
        override val value get() = threadLocal.get().value
        override fun isInitialized() = threadLocal.get().isInitialized()
    }
    

Here's a convenience function to create instances of the delegate:

fun <T> threadLocalLazy(provider: () -> T) = ThreadLocalLazy(provider)

Then just delegate a property to threadLocalLazy { ... }. Usage example:

class Example {
    val threadId by threadLocalLazy { Thread.currentThread().id }
}

fun main(args: Array<String>) {
    val example = Example()
    repeat(3) {
        thread {
            println(example.threadId) // should print three different numbers
        }
    }
}