我使用一个ExecutorService和一个Future (示例代码here)在一个单独的线程中运行一个带有超时的进程(线程“派生”发生在一个AOP方面)。
现在,主线程是一个Resteasy请求。Resteasy使用一个或多个ThreadLocal变量来存储一些上下文信息,我需要在Rest方法调用中的某个时刻检索这些信息。问题是,由于Resteasy线程是在新线程中运行的,所以ThreadLocal变量会丢失。
将Resteasy使用的任何ThreadLocal变量“传播”到新线程的最佳方式是什么?Resteasy似乎使用了不止一个ThreadLocal变量来跟踪上下文信息,我想“盲目”地将所有信息传输到新线程。
我研究过将ThreadPoolExecutor
子类化并使用beforeExecute方法将当前线程传递到池中,但我找不到将ThreadLocal变量传递到池中的方法。
有什么建议吗?
谢谢
发布于 2017-02-14 18:03:26
基于@erickson的回答,我写了这段代码。它正在为inheritableThreadLocals工作。它使用与在线程构造器中使用的相同方法来构建inheritableThreadLocals列表。当然,我使用反射来做到这一点。另外,我覆盖了executor类。
public class MyThreadPoolExecutor extends ThreadPoolExecutor
{
@Override
public void execute(Runnable command)
{
super.execute(new Wrapped(command, Thread.currentThread()));
}
}
包装器:
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();
}
}
}
复制方法:
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;
}
发布于 2011-09-01 00:22:32
据我所知,您可以看看InheritableThreadLocal,它的目的是将ThreadLocal
变量从父线程上下文传递到子线程上下文
发布于 2017-11-02 02:06:14
我不喜欢反射的方法。另一种解决方案是实现executor包装器,并将对象直接作为ThreadLocal
上下文传递给传播父上下文的所有子线程。
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);
}
https://stackoverflow.com/questions/7259906
复制相似问题