15

I have a problem which I don't really think has a solution but I'll try here anyway. My application uses a thread pool and some of the threads in this pool have an inheritable thread local variable. I've extended the ThreadPoolExecutor class to essentially clear out the thread local variable (in the afterExecute call back method) when a thread is done executing.

I understand that when you have an InheritableThreadLocal variable, the childValue() method is called when the thread is initialized to get the ThreadLocal variable's value from the parent thread. However, in my case the next time the thread is used (after being used once), the value of the InheritableThreadLocal variable is null (because it was previously cleared out in afterExecute). Is there a way to access the parent thread's thread local variable in beforeExecute so that I can essentially simulate what the childValue method in InheritableThreadLocal does at the time of thread creation.

neesh
  • 5,167
  • 6
  • 29
  • 32
  • 1
    Why are you clearing the child thread's thread local value if you need it later? Is the values needed on the next run from a different parent thread or have they changed in the parent since the child thread was first created? – Bert F Sep 04 '11 at 02:07
  • @Bert - that is precisely the reason. Basically I am storing a request id in the thread local variable which changes across multiple uses of the child thread. – neesh Sep 04 '11 at 03:04
  • Spring security doing some solution for the similar problem, but I didn't figure out yet what is it eactly! – Muhammad Hewedy Mar 14 '18 at 14:31

3 Answers3

8

It sounds like this is a poor use-case for the "inheritable" flavour of thread-locals.

My advice would be to just use a regular TheadLocal and do the initialization explicitly; e.g. passing the initial value from the parent thread to the child thread as a parameter or something.

(I was going suggest that you force initialization of the thread-local the child thread by having it fetch the value as soon as it starts. But that risks a race-condition; e.g. if the parent thread is returned to the pool before the child thread starts executing.)


I guess what I am asking is if there is a way to access the value of the parent thread's thread local variable from a child thread.

There isn't a way to do this.

And, judging from your other comments, I doubt that you mean "parent" and "child" in the normal sense ... where the parent thread creates the child thread.

But here's an idea. Instead of trying to share a variable between threads, share a fixed value (e.g. a request ID), and use that as a key for a shared Map. Use the Map entries as the shared variables.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • 1
    thanks for the response. I am curious how I could force initialize the thread-local of the child thread. I guess what I am asking is if there is a way to access the value of the parent thread's thread local variable from a child thread. – neesh Sep 06 '11 at 15:11
  • Unfortunately this answer leaves the problem open. How can a thread local be transferred from one thread to another. If the one is a child of the other then the InheritableThreadLocals do the job nicely, but what do you do in case of a thread pool? – Gregor Aug 01 '17 at 15:10
  • Why did you downvote the answer? It is kind of rude to downvote an answer that states that no good solution exists ... just because you don't like that fact. Certainly, it doesn't encourage me to help you! – Stephen C Aug 01 '17 at 15:34
  • There must be a "real" answer to that question. I know that somehow correlators can be shared between threads to log events resulting from the same request. It's not meant to be rude to -1 the answer, I just express that according to me, this is not the answer. – Gregor Aug 02 '17 at 06:32
  • Noted .... I remain not encouraged. And it is the answer. Just not the answer that you want. – Stephen C Aug 02 '17 at 09:26
  • I think here's a practical implementation from spring-cloud-slueth: https://github.com/spring-cloud/spring-cloud-sleuth/blob/master/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/async/TraceRunnable.java – Muhammad Hewedy Mar 15 '18 at 09:09
2

The constructor of a runnable runs in the thread of the caller, whereas the run method runs in the child thread. You can use this fact to transfer information from the parent to the child thread. See:

public class ThreadlocalApplication {
static public ThreadLocal<String> tenantId = new ThreadLocal<>();

public static void main(String[] args) throws ExecutionException, InterruptedException {
    ExecutorService executor = Executors.newCachedThreadPool();
    System.out.println(Thread.currentThread().getName());
    ThreadlocalApplication.tenantId.set("4711");
    executor.submit(new AsyncTask()).get();
    executor.shutdown();
}

static class AsyncTask implements Runnable {
    private String _tenantId;

    public AsyncTask() {
        System.out.println(Thread.currentThread().getName());
        _tenantId = ThreadlocalApplication.tenantId.get();
    }

    @Override
    public void run() {
        ThreadlocalApplication.tenantId.set(_tenantId);
        System.out.println(Thread.currentThread().getName());
        System.out.println(ThreadlocalApplication.tenantId.get());
    }
}
}

And this is the result

main
main
pool-1-thread-1
4711
Gregor
  • 2,917
  • 5
  • 28
  • 50
  • I like this answer a lot and consider it clearer and straightforward than @Stephen C answer. But what about Thread safety for the private variables (`_tenantId`) here? – Muhammad Hewedy Mar 15 '18 at 11:19
0

I had a similar problem and ended up creating a ForkListeningExecutorService which wraps an ExecutorService (see here). The wrapper sends events to a listener whenever a task is submitted to the executor or when it ends. You can then use the listener to pass whatever you want across the threads. This is how the Listener looks like:

  /**
   * Listener that will receive the events around task submission.
   *
   */
  public interface ExecutorServiceListener {

    /**
     * Will be called <b>before</b> any task is submitted to the service. This method will therefore run on the original "parent" thread.
     */
    default void beforeTaskSubmission() {
      // to be overridden by implementations
    }

    /**
     * Will be called <b>after</b> a task is submitted to the service and <b>before</b> the actual execution starts. This method will therefore run on the new "child" thread.
     */
    default void afterTaskSubmission() {
      // to be overridden by implementations
    }

    /**
     * Will be called <b>before</b> a submitted task ends no matter if an exception was thrown or not. This method will therefore run on the new "child" thread just before it's
     * released.
     */
    default void beforeTaskEnds() {
      // to be overridden by implementations
    }
  }

So, instead of extending ThreadPoolExecutor, you can simply wrap it (or whatever other ExecutorService implementation) and pass the state within the listener like this:

public class ForkListeningExecutorServiceExample {

  private static ThreadLocal<String> threadLocal = new ThreadLocal<>();

  private static void printThreadMessage(final String message) {
    System.out.println(message
        + ", current thread: " + Thread.currentThread().getName()
        + ", value from threadLocal: " + threadLocal.get());
  }

  public static void main(final String[] args) throws Exception {

    threadLocal.set("MY_STATE");

    final ExecutorService executorService = new ForkListeningExecutorService(
        Executors.newCachedThreadPool(),
        new ExecutorServiceListener() {

          private String valueToShare;

          @Override
          public void beforeTaskSubmission() {
            valueToShare = threadLocal.get();
            printThreadMessage("The task is about to be submitted");
          }

          @Override
          public void afterTaskSubmission() {
            threadLocal.set(valueToShare);
            printThreadMessage("The task has been submitted and will start now");
          }

          @Override
          public void beforeTaskEnds() {
            threadLocal.set(null);
            printThreadMessage("The task has finished and thread will be released now");
          }
        });

    executorService.submit(() -> {
      printThreadMessage("The task is running now");
    }).get();

    printThreadMessage("We are back on the main thread");
  }
}

The output looks then like this:

The task is about to be submitted, current thread: main, value from threadLocal: MY_STATE
The task has been submitted and will start now, current thread: pool-1-thread-1, value from threadLocal: MY_STATE
The task is running now, current thread: pool-1-thread-1, value from threadLocal: MY_STATE
The task has finished and thread will be released now, current thread: pool-1-thread-1, value from threadLocal: null
We are back on the main thread, current thread: main, value from threadLocal: MY_STATE
Alex
  • 1,126
  • 1
  • 11
  • 24