2

I would like to override behaviour so that ExecutorService calls custom method. When a thread is released I would like to clear all ThreadLocal variables. Not very familiar with api or maybe there is something which exists there already.

Not sure how thread pool manages threads when they finished their job but I assume it does not destroy them as that would be expensive if it does not destroy them then based on ThreadLocal description:

Each thread holds an implicit reference to its copy of a thread-local
 * variable as long as the thread is alive and the {@code ThreadLocal}
 * instance is accessible; after a thread goes away, all of its copies of
 * thread-local instances are subject to garbage collection (unless other
 * references to these copies exist).

I need to clear up ThreadLocal

Aubergine
  • 5,862
  • 19
  • 66
  • 110

3 Answers3

2

For an ExecutorService you could make a self cleaning task.

public CleanerTask implements Runnable {
    private Disposable realRunnable;

    public CleanerTask(Disposable d) {
        realRunnable = d;
    }

    public void run() {
        realRunnable.run();
        realRunnable.dispose();
    }
}

In this example Disposable is an interface extending Runnable and providing a dispose() method that cleans the ThreadLocal variables. The implementation guarantees that run() and dispose() are run in the same thread, so the variables can safely be cleared.

Then you just need to make sure you wrap your tasks in a CleanerTask before submitting them to your executor.


However if you're not tied to ExecutorService you can extend ThreadPoolExecutor which provides an afterExecute method. Then you just call dispose() there (after checking that the Runnable is of the correct type).

(I first thought afterExecute wasn't run in the thread that ran the task, but luckily I thought wrong.)

Kayaman
  • 72,141
  • 5
  • 83
  • 121
0

Not sure if you are thinking about threads and thread pools in a right way. Threads are started with start() and when their execution is finished, they are destroyed. How and when the threads are created depends on your executor service implementation... You might have executor service that just runs tasks in the current thread. Pooled executor service might start its threads with infinite loop waiting for submitted tasks... however even thread pools are usually flexible in a way that the pool keeps only a limited number of waiting threads and if there are more threads it lets them die (breaks the infinite sleep loop). Also usually if the execution throws an exception, the thread is discarded as well.

Having thread-locals survive a single execution is not a good practice. You should clean up your thread-locals after every execution. Do not wait for thread disposal / destruction.

TL;DR Do not try to hack into "thread destruction", but rather start every execution with try/finally to set-up and clean your thread locals.

Pavel Horal
  • 17,782
  • 3
  • 65
  • 89
  • This has nothing to do with thread destruction. If the thread is destroyed, so are the threadlocals. But a threadpool that recycles threads (which is highly likely) would need to make sure that threadlocals are cleared after **task** execution, which I described in my answer. – Kayaman May 17 '18 at 12:41
  • I agree that thread locals need to be cleared after every execution. Hence why I added TLDR section. My point is that you should ideally clear thread locals in the same method where they are initialized (`try/finally`) and not depend on thread lifecycle (original question contains the following sentence: *When a thread is released I would like to clear all ThreadLocal variables.*) – Pavel Horal May 17 '18 at 12:59
  • You're right, you did address the issues. And of course the task itself should be responsible that any ThreadLocals are released. I put the "when a thread is released" down as general confusion between thread vs. task. – Kayaman May 17 '18 at 13:11
0

Threads will get reused by an executorservice that implements a threadpool, those threadlocal entries will stay with the thread across tasks unless removed. If you know when a task is done that its Threadlocal value is now irrelevant, you can clean it up like Kayaman says.

But the point of Threadlocals is that they are available across different components, for cases where the different components can't manage its scope. For instance a web application could put something in a threadlocal in a filter on the way in with a HTTP request, have it available to web controllers and services, etc., over the course of the request, and clean up the threadlocal in the filter on the way back out. So in this example the scope of the threadlocal value is managed by the filter in order to be available to everything participating in the request for that thread, where in a "normal" (meaning not some async non-blocking setup like Play) web application the request is handled by one thread in the application server.

If it's that straightforward for you to identify the scope where the ThreadLocal value isn't needed anymore that the task can clean it up, then it sounds like your code is using ThreadLocals unnecessarily. I'd suggest removing these ThreadLocals and using local variables within the task instead. A ThreadLocal shouldn't be used as an easy alternative to argument-passing.

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276