Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

InheritableThreadLocal value not inherited by ExecutorService threads

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 ?

like image 880
nfsquake Avatar asked Dec 23 '16 00:12

nfsquake


1 Answers

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.

like image 78
Sotirios Delimanolis Avatar answered Oct 13 '22 01:10

Sotirios Delimanolis