Thread Pooling and InheritedThreadLocal

1.7k Views Asked by At

I have seen the below question.The answer was to use the semaphores instead.This didnot answer one of the other problems stated in the question which I am facing.

Using InheritableThreadLocal with ThreadPoolExecutor -- or -- a ThreadPoolExecutor that doesn't reuse threads

I have a parent thread which sets some unique identifier for each new request in an InhertiedThreadLocal and submits 2 Runnable Tasks to the ThreadPool i.e for 2 threads. For the initial request the values that are set for InheritedThreadLocal in the parent thread are propagated to the ChildThread correctly. For the next requests,the childthreads are not receiving the latest InheritedThreadLocal set by the parent Thread and the old values in the ChildThread are used.

This is because threadpool reuses threads and the InheritedThreadLocal are copied only when new thread is created.

Now how can I propagate the latest value of InheritedThreadLocal from parent to Child thread in the thread pool scenario. Is there a way out for this ?

2

There are 2 best solutions below

2
On

If we configure ThreadPoolExecutor like this

new ThreadPoolExecutor(0, 2, 0L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());

it will create a new Thread each time thus InheritableThreadLocals will be inherited, though then it can hardly be called a ThreadPoolExecutor

0
On

I wrote these methods as I needed it.

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ThreadPoolExecutor;

public class EnvUtils {
    /**
     * Extract the current inheritableThreadLocals map from the current thread.
     * Typical usage is in a threadpool, where:
     * <ul>
     * <li>You run {@link EnvUtils#extract()} in the running thread, to store
     * the information somewhere
     * <li>You create a method {@link ThreadPoolExecutor#beforeExecute()} in which
     * you run {@link EnvUtils#copy(Object)} with the above-stored information.
     * </ul>
     *
     * @return The current inheritableThreadLocals of the current thread
     */
    public static Object extract() {
        Object toreturn = null;
        try {
            // get field descriptor
            Field inthlocalsField = Thread.class.getDeclaredField("inheritableThreadLocals");
            inthlocalsField.setAccessible(true);
            //the object stored there
            Object inthlocalsMap = inthlocalsField.get(Thread.currentThread());
            // no need to copy, it will be done by the copy() method below
            toreturn = inthlocalsMap;
        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
            // This may happen in a different Java implementation
            throw new RuntimeException(e);
        }
        return toreturn;
    }

    /**
     * Replaces the field inheritableThreadLocals of the current thread with the values provided.<br/>
     * It is the same as if the current thread was just created from the thread whose <code>stored</code>
     * values come from.<br/>
     * Must be called in the thread which want to inherit from given {@link inheritableThreadLocals} map.<br/>
     * <b>Note 1:</b> This does not modify non-inheritable thread locals<br/>
     * <b>Note 2:</b> This delete all previous values of {@link inheritableThreadLocals} in the current thread<br/>
     *
     * @param stored
     *            The stored inheritableThreadLocals value, coming from the extract() method
     */
    public static void copy(final Object stored) {
        try {
            // find ThreadLocalMap class
            String threadLocalClassName = ThreadLocal.class.getName();
            Class<?> threadLocaLMapClass = Class.forName(threadLocalClassName + "$ThreadLocalMap");
            // check that given object is an instance of the class
            if (stored == null || !threadLocaLMapClass.isInstance(stored)) {
                throw new IllegalArgumentException("Given object is not a ThreadLocalMap: " + stored);
            }
            // get constructor of ThreadLocalMap
            Constructor<?> creator = threadLocaLMapClass.getDeclaredConstructor(threadLocaLMapClass);
            creator.setAccessible(true);
            // get field descriptor of the thread
            Field inthlocalsField = Thread.class.getDeclaredField("inheritableThreadLocals");
            inthlocalsField.setAccessible(true);
            // create new inherited map
            Object newObj = creator.newInstance(stored);
            // set it to the current thread
            inthlocalsField.set(Thread.currentThread(), newObj);

        } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | NoSuchFieldException
                | IllegalAccessException | IllegalArgumentException | InvocationTargetException
                | InstantiationException e) {
            // This may happen in a different Java implementation
            throw new RuntimeException(e);
        }

    }

}

EnvUtils.extract() returns a reference to the current thread's inheritableThreadLocals map.

Now when you create a Runnable to be called in a ThreadPool, just store in a field inheritableThreadInfo = EnvUtils.extract(), and in its run() method, just call EnvUtils.copy(inheritableThreadInfo).

Note: This solution uses a lot of reflection and as such it is subject to the Java implementation. I tested on Oracle Java 1.8.