InheritableThreadLocal extends ThreadLocal and used when we need to pass parent thread-local attribute values automatically to child thread on creation.
This inheritance works perfectly fine when you are creating new child thread each time and not reusing already created thread.
Let us consider a scenario -
We have thread pool of size 5.
When we send first 5 tasks to thread pool to process for each task new thread is created so inheritance of thread-local attribute works perfectly fine.
Problem starts from 6th request.
When you send 6th task no new thread gets created but already created thread from thread pool is reused to process it.
On 6th request parent thread-local attribute values doesn't get inherited to the child thread as we are not creating new thread but reusing already created thread from pool.
This code snippet will explain the point -
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class DeadlyComboTest {
public static void main(String[] args) throws InterruptedException {
int noOfThreads = 5;
//Execution 1
int threadPoolSize = noOfThreads;
//Execution 2 : uncomment below line and comment line above
//int threadPoolSize = noOfThreads/2;
//1. create thread pool
ExecutorService executor = Executors.newFixedThreadPool(threadPoolSize);
//2. create Inheritable thread local
InheritableThreadLocal<Object> value = new InheritableThreadLocal<>();
//3. create new command and execute using thread pool created in step 1
for (int i = 1; i <= noOfThreads; i++) {
value.set(i);
executor.execute(() -> printThreadLocalValue(value));
}
executor.shutdown();
}
private static void printThreadLocalValue(ThreadLocal<Object> value) {
System.out.println(Thread.currentThread().getName() + " = " + value.get());
}
}
Exection 1: noOfThreads = threadPoolSize = 5
OUTPUT: you may get the output in different sequence
pool-1-thread-1 = 1
pool-1-thread-2 = 2
pool-1-thread-3 = 3
pool-1-thread-4 = 4
pool-1-thread-5 = 5
Everything looks good.
As on every request new thread get created and child thread correctly
inherits the thread-local values of parent.
Execution 2: noOfThreads = 5 && threadPoolSize = noOfThreads/2 = 2
OUTPUT:
pool-1-thread-1 = 1
pool-1-thread-2 = 2
pool-1-thread-1 = 1
pool-1-thread-2 = 2
pool-1-thread-1 = 1
Only for first two requests thread-local inheritance works correctly,
as thread pool size is 2 so for first two requests new threads
get created and pooled.
Third request onward already created threads from pool get reused
which still have the inherited values of old thread.
Execution 2 is a real world scenario where threads from pool will get reused.
What can go bad when we use Thread pool and InheritableThreadLocal combination?
It will cause unintended information leak as worker/child thread from pool will leak thread-local attribute values of one thread to another.
If your business logic is dependent on these attribute values, you will get hard to track bugs.
That's why spring documentation warns against using this combo of Thread pool and InheritableThreadLocal.