40

I'm running a process in a separate thread with a timeout, using an ExecutorService and a Future (example code here) (the thread "spawning" takes place in a AOP Aspect).

Now, the main thread is a Resteasy request. Resteasy uses one ore more ThreadLocal variables to store some context information that I need to retrieve at some point in my Rest method call. Problem is, since the Resteasy thread is running in a new thread, the ThreadLocal variables are lost.

What would be the best way to "propagate" whatever ThreadLocal variable is used by Resteasy to the new thread? It seems that Resteasy uses more than one ThreadLocal variable to keep track of context information and I would like to "blindly" transfer all the information to the new thread.

I have looked at subclassing ThreadPoolExecutor and using the beforeExecute method to pass the current thread to the pool, but I couldn't find a way to pass the ThreadLocal variables to the pool.

Any suggestion?

Thanks

AlikElzin-kilaka
  • 34,335
  • 35
  • 194
  • 277
Luciano Fiandesio
  • 10,037
  • 10
  • 48
  • 56
  • Could you rewrite the second paragraph slightly? It confusing to me. Also, what's wrong with beforeExecute? You could not get it to work properly, or you realized it cannot fit your need? – toto2 Aug 31 '11 at 16:36

6 Answers6

24

The set of ThreadLocal instances associated with a thread are held in private members of each Thread. Your only chance to enumerate these is to do some reflection on the Thread; this way, you can override the access restrictions on the thread's fields.

Once you can get the set of ThreadLocal, you could copy in the background threads using the beforeExecute() and afterExecute() hooks of ThreadPoolExecutor, or by creating a Runnable wrapper for your tasks that intercepts the run() call to set an unset the necessary ThreadLocal instances. Actually, the latter technique might work better, since it would give you a convenient place to store the ThreadLocal values at the time the task is queued.


Update: Here's a more concrete illustration of the second approach. Contrary to my original description, all that is stored in the wrapper is the calling thread, which is interrogated when the task is executed.

static Runnable wrap(Runnable task)
{
  Thread caller = Thread.currentThread();
  return () -> {
    Iterable<ThreadLocal<?>> vars = copy(caller);
    try {
      task.run();
    }
    finally {
      for (ThreadLocal<?> var : vars)
        var.remove();
    }
  };
}

/**
 * For each {@code ThreadLocal} in the specified thread, copy the thread's 
 * value to the current thread.  
 * 
 * @param caller the calling thread
 * @return all of the {@code ThreadLocal} instances that are set on current thread
 */
private static Collection<ThreadLocal<?>> copy(Thread caller)
{
  /* Use a nasty bunch of reflection to do this. */
  throw new UnsupportedOperationException();
}
erickson
  • 265,237
  • 58
  • 395
  • 493
  • Could you give an example how the latter technique would work ? – Viraj Feb 08 '16 at 23:40
  • @erickson is your "nasty bunch of reflection to do this" something along the lines of http://stackoverflow.com/a/32231177/131929? – Marcel Stör Feb 22 '16 at 20:06
  • @MarcelStör I haven't tried it, but it looks the right information. So, in addition to reading the `value` field, you'll need to call `get()` on the entry to get the `ThreadLocal` instance referenced by the entry. Once you have these two objects, you are done with reflection. Call `local.set(value)` for each entry that you find, and add the thread local to the collection that is returned from `copy()`. – erickson Feb 22 '16 at 20:44
  • 1
    @MarcelStör As I look at that a bit more, there could actually be a visibility problem in the concurrent access of the thread local map, because its designed for single-threaded access. This means that you'd need to copy the values in the current thread, and then set them in the worker threads, conveying them between threads in a safe way. I will have to think about this some more and amend my answer. – erickson Feb 22 '16 at 20:50
  • @MarcelStör After more thought, I think in this particular case, everything is okay. Submitting the task to an `Executor` will take care of the visibility issues, because there is a memory barrier there. And because the main thread is waiting while the background thread runs, the `ThreadLocals` of the main thread will not be altered in the meantime. However, if those conditions changed, there could be some additional work necessary. – erickson Feb 22 '16 at 21:05
  • 1
    @erickson one issue with copying too late is the ThreadLocal's will change later in typical platforms as that thread processes the next request so I think you have to copy on the thread before that race condition occurs. – Dean Hiller Aug 09 '16 at 00:04
  • @bit_cracker007 `InheritableThreadLocal` copies values from the current thread at the time the new thread is created. In the use case here, we a submitting tasks to an executor with worker threads already created. So some other means are needed. – erickson Jul 28 '21 at 00:12
  • @erickson Is there no way to ensure Child threads to share the exact same reference of the shared context object as parent thread, so that any set done by child thread is visible to parent thread? Question posted: https://stackoverflow.com/questions/68549187/how-to-share-parent-threadlocal-object-reference-with-the-child-threads – bit_cracker007 Jul 28 '21 at 01:02
5

Based on @erickson answer I wrote this code. It is working for inheritableThreadLocals. It builds list of inheritableThreadLocals using same method as is used in Thread contructor. Of course I use reflection to do this. Also I override the executor class.

public class MyThreadPoolExecutor extends ThreadPoolExecutor
{
   @Override
   public void execute(Runnable command)
   {
      super.execute(new Wrapped(command, Thread.currentThread()));
   }
}

Wrapper:

   private class Wrapped implements Runnable
   {
      private final Runnable task;

      private final Thread caller;

      public Wrapped(Runnable task, Thread caller)
      {
         this.task = task;
         this.caller = caller;
      }

      public void run()
      {
         Iterable<ThreadLocal<?>> vars = null;
         try
         {
            vars = copy(caller);
         }
         catch (Exception e)
         {
            throw new RuntimeException("error when coping Threads", e);
         }
         try {
            task.run();
         }
         finally {
            for (ThreadLocal<?> var : vars)
               var.remove();
         }
      }
   }

copy method:

public static Iterable<ThreadLocal<?>> copy(Thread caller) throws Exception
   {
      List<ThreadLocal<?>> threadLocals = new ArrayList<>();
      Field field = Thread.class.getDeclaredField("inheritableThreadLocals");
      field.setAccessible(true);
      Object map = field.get(caller);
      Field table = Class.forName("java.lang.ThreadLocal$ThreadLocalMap").getDeclaredField("table");
      table.setAccessible(true);

      Method method = ThreadLocal.class
              .getDeclaredMethod("createInheritedMap", Class.forName("java.lang.ThreadLocal$ThreadLocalMap"));
      method.setAccessible(true);
      Object o = method.invoke(null, map);

      Field field2 = Thread.class.getDeclaredField("inheritableThreadLocals");
      field2.setAccessible(true);
      field2.set(Thread.currentThread(), o);

      Object tbl = table.get(o);
      int length = Array.getLength(tbl);
      for (int i = 0; i < length; i++)
      {
         Object entry = Array.get(tbl, i);
         Object value = null;
         if (entry != null)
         {
            Method referentField = Class.forName("java.lang.ThreadLocal$ThreadLocalMap$Entry").getMethod(
                    "get");
            referentField.setAccessible(true);
            value = referentField.invoke(entry);
            threadLocals.add((ThreadLocal<?>) value);
         }
      }
      return threadLocals;
   }
drkicknrush
  • 133
  • 8
Mr Jedi
  • 33,658
  • 8
  • 30
  • 40
1

I don't like Reflection approach. Alternative solution would be to implement executor wrapper and pass object directly as a ThreadLocal context to all child threads propagating a parent context.

public class PropagatedObject {

    private ThreadLocal<ConcurrentHashMap<AbsorbedObjectType, Object>> data = new ThreadLocal<>();

   //put, set, merge methods, etc

}

==>

public class ObjectAwareExecutor extends AbstractExecutorService {

    private final ExecutorService delegate;
    private final PropagatedObject objectAbsorber;

    public ObjectAwareExecutor(ExecutorService delegate, PropagatedObject objectAbsorber){
        this.delegate = delegate;
        this.objectAbsorber = objectAbsorber;
    }
    @Override
    public void execute(final Runnable command) {

        final ConcurrentHashMap<String, Object> parentContext = objectAbsorber.get();
        delegate.execute(() -> {
            try{
                objectAbsorber.set(parentContext);
                command.run();
            }finally {
                parentContext.putAll(objectAbsorber.get());
                objectAbsorber.clean();
            }
        });
        objectAbsorber.merge(parentContext);
    }
Wild Goat
  • 3,509
  • 12
  • 46
  • 87
1

As I understand your problem, you can have a look at InheritableThreadLocal which is meant to pass ThreadLocal variables from Parent Thread context to Child Thread Context

Grooveek
  • 10,046
  • 1
  • 27
  • 37
  • 27
    Won't work, first of all the OP does not have control over `ThreadLocal` creation in 3rd party library. Secondly, `ExecutorService` reuses threads, while `InheritableThreadLocal` works only when you spawn new thread directly. – Tomasz Nurkiewicz Aug 31 '11 at 16:24
0

Here is an example to pass the current LocaleContext in parent thread to the child thread spanned by CompletableFuture[By default it used ForkJoinPool].

Just define all the things you wanted to do in a child thread inside a Runnable block. So when the CompletableFuture execute the Runnable block, its the child thread who is in control and voila you have the parent's ThreadLocal stuff set in Child's ThreadLocal.

The problem here is not the entire ThreadLocal is copied over. Only the LocaleContext is copied. Since the ThreadLocal is of private access to only the Thread it belongs too using Reflection and trying to get and set in Child is all too much of wacky stuff which might lead to memory leaks or performance hit.

So if you know the parameters you are interested from the ThreadLocal, then this solution works way cleaner.

 public void parentClassMethod(Request request) {
        LocaleContext currentLocale = LocaleContextHolder.getLocaleContext();
        executeInChildThread(() -> {
                LocaleContextHolder.setLocaleContext(currentLocale);
                //Do whatever else you wanna do
            }));

        //Continue stuff you want to do with parent thread
}


private void executeInChildThread(Runnable runnable) {
    try {
        CompletableFuture.runAsync(runnable)
            .get();
    } catch (Exception e) {
        LOGGER.error("something is wrong");
    }
}
Seetha
  • 980
  • 1
  • 8
  • 27
-4

If you look at ThreadLocal code you can see:

    public T get() {
        Thread t = Thread.currentThread();
        ...
    }

current thread cannot be overwritten.

Possible solutions:

  1. Look at java 7 fork/join mechanism (but i think it's a bad way)

  2. Look at endorsed mechanism to overwrite ThreadLocal class in your JVM.

  3. Try to rewrite RESTEasy (you can use Refactor tools in your IDE to replace all ThreadLocal usage, it's look like easy)

Alexey Kutuzov
  • 679
  • 8
  • 22