java - 线程池和 InheritedThreadLocal

标签 java threadpool thread-local inherited

我看到了下面的问题。答案是改用信号量。这没有回答我面临的问题中所述的其他问题之一。

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

我有一个父线程,它为 InhertiedThreadLocal 中的每个新请求设置一些唯一标识符,并向 ThreadPool 提交 2 个可运行任务,即 2 个线程。 对于初始请求,在父线程中为 InheritedThreadLocal 设置的值会正确传播到 ChildThread。 对于下一个请求,子线程不会收到父线程设置的最新 InheritedThreadLocal,而是使用 ChildThread 中的旧值。

这是因为线程池重用了线程,并且只有在创建新线程时才会复制 InheritedThreadLocal。

现在如何在线程池场景中将 InheritedThreadLocal 的最新值从父线程传播到子线程。 有解决办法吗?

最佳答案

我根据需要编写了这些方法。

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() 返回对当前线程的 inheritableThreadLocals 映射的引用。

现在,当您创建一个 Runnable 以在 ThreadPool 中调用时,只需将其存储在一个字段中 inheritableThreadInfo = EnvUtils.extract(),在它的 run() 方法中,只需调用 EnvUtils.copy(inheritableThreadInfo)

注意:此解决方案使用了大量反射,因此它受制于 Java 实现。我在 Oracle Java 1.8 上进行了测试。

关于java - 线程池和 InheritedThreadLocal,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18129039/

相关文章:

java - 如何在solrJ中使用termVector

c# - 这是使用线程池的正确案例吗?

Python多线程并没有提高速度

java - 如何为声明为 ThreadLocal 的变量的多个副本保持一致性?

java - MapStruct 不生成实现类

java - 为什么 Java 的 String.getBytes() 使用 "ISO-8859-1"

Java-获取线程状态

java - java中ThreadLocal变量如何将其对象的副本传递给不同的线程?

java - 使用 ThreadLocal 绕过 Servlet 线程不安全?

java - 为 JVM 禁用 "internet access"?