InheritableThreadLocal value not inherited by ExecutorService threads

孤者浪人 提交于 2019-11-30 09:55:58

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 look at the thread names in the output, you can see that there are two threads(as per your ExecutionContext configuration) pool-1-thread-1 and pool-1-thread-2.

444 thread is reusing the thread pool-1-thread-2 which was previously used by 222 with tl already assigned.

333 thread is reusing pool-1-thread-1 which was previously used by 111 with tl already assigned, but it's overwriting the inherited InitialValue to NewInitialValue.

You might see a different output if you increase the no. of threads. This is what I get with

three threads:

111 Thread[pool-1-thread-1,5,main]InitialValue
222 Thread[pool-1-thread-2,5,main]InitialValue
333 Thread[pool-1-thread-3,5,main]NewInitialValue
444 Thread[pool-1-thread-2,5,main]InitialValue // Reusing "pool-1-thread-2"

four threads:

111 Thread[pool-1-thread-1,5,main]InitialValue
222 Thread[pool-1-thread-2,5,main]InitialValue
333 Thread[pool-1-thread-3,5,main]NewInitialValue
444 Thread[pool-1-thread-4,5,main]NewInitialValue // Fresh thread "pool-1-thread-4"

I am using Spring Integration and using an executor to process split messages. Faced the same issue during this.

@Soritos is right

That's hard to answer without knowing what you want to do

What workaround I did was

  1. Added the ThreadLocal variable in MessageHeaders.
  2. In the message Splitter, created a new InhertiableThreadLocal and assign value from the MessageHeaders

     if (null != message.getHeaders().get("frameworkCorrelationID")) {
        private static final InheritableThreadLocal<String> id = new InheritableThreadLocal(); 
        id.set((String)message.getHeaders().get("frameworkCorrelationID"));    }
    
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!