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 ?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With