import java.util.concurrent.Executors
import scala.concurrent.{ExecutionContext, Future}
object TestInheritableThreadLocal {
def main(args: Array[String]): Unit = {
implicit val ec = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(2))
val tl: InheritableThreadLocal[String] = new InheritableThreadLocal[String]()
tl.set("InitialValue")
Future {
println("111 " + Thread.currentThread() + tl.get())
Future {
println("222 " + Thread.currentThread() + tl.get())
}
}
Thread.sleep(3000)
Future {
tl.set("NewInitialValue")
println("333 " + Thread.currentThread() + tl.get())
Future {
println("444 " + Thread.currentThread() + tl.get())
}
Thread.sleep(3000)
}
}
}
Output
111 Thread[pool-1-thread-1,5,main]InitialValue
222 Thread[pool-1-thread-2,5,main]InitialValue
333 Thread[pool-1-thread-1,5,main]NewInitialValue
444 Thread[pool-1-thread-2,5,main]InitialValue
I was expecting "NewInitialValue" in the last line of output since 333 Thread spawned of Thread 444 and tl is a Inheritable thread local.
What is causing this issue and how can it be resolved ?
You shouldn't rely on InheritableThreadLocal
when you don't have control over the creation of threads. The javadoc states:
[...] when a child thread is created, the child receives initial values for all inheritable thread-local variables for which the parent has values.
In your example, threads are being created by the ExecutorService
returned by Executors.newFixedThreadPool(2)
That's an executor that will use up to two threads to execute your tasks. From the javadoc
Creates a thread pool that reuses a fixed number of threads operating off a shared unbounded queue. At any point, at most
nThreads
threads will be active processing tasks. If additional tasks are submitted when all threads are active, they will wait in the queue until a thread is available.
This is an implementation detail, but those threads are created lazily, as needed. When you submit the first task, 111
, the call to submit
will create and start a new thread. This new thread will inherit the value InitialValue
. Similarly, when this thread submits the second task, 222
, its call to submit
will force the creation of the second thread which will also inherit the InitialValue
.
Then you submit the third task, 333
, overwrite the InheritableThreadLocal
's value and print it. When you submit the fourth task 444
, the ExecutorService
uses existing threads to execute it. That thread already has a value, inherited earlier.
how can it be resolved
That's hard to answer without knowing what you want to do. But, if you want to effectively use InheritableThreadLocal
, it all comes down to knowing and controlling the creation of threads, and therefore the inheritance chain.
You could create and use an ExecutorService
that creates and uses a new thread for each submitted task, for example.
Similarly, you could use another mechanism to propagate that value: an AtomicReference
or lambda capture of an immutable value.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With