Start here. This is the direct spoken answer to practice first.
Overview
ThreadLocal scopes a value to a thread, but the value can outlive the request when the thread itself is reused.
ThreadLocal gives each thread its own value for a shared key. It can support thread-confined state or infrastructure context when passing a value through every call would be impractical. In a thread pool, the thread outlives a request, so a value that is not removed can leak memory or appear in a later request. Cleanup belongs in a finally block at the boundary that set the value.