首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >将ThreadLocal传播到从ExecutorService获取的新线程

将ThreadLocal传播到从ExecutorService获取的新线程
EN

Stack Overflow用户
提问于 2011-09-01 00:02:53
回答 4查看 33.1K关注 0票数 35

我使用一个ExecutorService和一个Future (示例代码here)在一个单独的线程中运行一个带有超时的进程(线程“派生”发生在一个AOP方面)。

现在,主线程是一个Resteasy请求。Resteasy使用一个或多个ThreadLocal变量来存储一些上下文信息,我需要在Rest方法调用中的某个时刻检索这些信息。问题是,由于Resteasy线程是在新线程中运行的,所以ThreadLocal变量会丢失。

将Resteasy使用的任何ThreadLocal变量“传播”到新线程的最佳方式是什么?Resteasy似乎使用了不止一个ThreadLocal变量来跟踪上下文信息,我想“盲目”地将所有信息传输到新线程。

我研究过将ThreadPoolExecutor子类化并使用beforeExecute方法将当前线程传递到池中,但我找不到将ThreadLocal变量传递到池中的方法。

有什么建议吗?

谢谢

EN

回答 4

Stack Overflow用户

发布于 2017-02-14 18:03:26

基于@erickson的回答,我写了这段代码。它正在为inheritableThreadLocals工作。它使用与在线程构造器中使用的相同方法来构建inheritableThreadLocals列表。当然,我使用反射来做到这一点。另外,我覆盖了executor类。

代码语言:javascript
复制
public class MyThreadPoolExecutor extends ThreadPoolExecutor
{
   @Override
   public void execute(Runnable command)
   {
      super.execute(new Wrapped(command, Thread.currentThread()));
   }
}

包装器:

代码语言:javascript
复制
   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();
         }
      }
   }

复制方法:

代码语言:javascript
复制
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;
   }
票数 5
EN

Stack Overflow用户

发布于 2011-09-01 00:22:32

据我所知,您可以看看InheritableThreadLocal,它的目的是将ThreadLocal变量从父线程上下文传递到子线程上下文

票数 1
EN

Stack Overflow用户

发布于 2017-11-02 02:06:14

我不喜欢反射的方法。另一种解决方案是实现executor包装器,并将对象直接作为ThreadLocal上下文传递给传播父上下文的所有子线程。

代码语言:javascript
复制
public class PropagatedObject {

    private ThreadLocal<ConcurrentHashMap<AbsorbedObjectType, Object>> data = new ThreadLocal<>();

   //put, set, merge methods, etc

}

==>

代码语言:javascript
复制
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);
    }
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7259906

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档