Java Concurrency Interview

Executors and thread pools question 88 of 180 senior

Why must a ThreadLocal be removed in a pooled thread?

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.

executors threadlocal memory-leak

Quiz yourself on Executors and thread pools Suggest a correction