Executors and thread pools question 88 of 180 senior
Why must a ThreadLocal be removed in a pooled thread?
- It does not have to be, the GC handles it
- Correct: Because the thread outlives the task, so the value stays reachable and leaks, and the next unrelated task on that thread can read it
- Because
ThreadLocalis deprecated - Because it is not thread safe
A ThreadLocal entry lives in a map owned by the thread. In a pool the thread is reused indefinitely,
so the value is never collected, which in an application server means a retained classloader and the
familiar "PermGen/Metaspace leak after redeploy". Worse than the leak: task B sees the tenant id, user
principal or transaction that task A left behind.
The keys in ThreadLocalMap are weak references but the values are not, so dropping the
ThreadLocal object does not free the value until that slot happens to be cleaned. The discipline is
a finally:
try {
context.set(requestContext);
handle(request);
} finally {
context.remove();
}
ScopedValue is the intended replacement: immutable, bounded to a scope, and inherited by
structured concurrency forks without any of this. It was a preview API from Java 21 and is final since
Java 25 (JEP 506); structured concurrency is still a preview there.
Quiz yourself on Executors and thread pools Suggest a correction