- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我看到了下面的问题。答案是改用信号量。这没有回答我面临的问题中所述的其他问题之一。
我有一个父线程,它为 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/
我是一名优秀的程序员,十分优秀!