在ThreadPoolExecutor中使用InheritableThreadLocal - 或者 - 不重用线程的ThreadPoolExecutor

时间:2012-01-26 00:14:58

标签: java multithreading executorservice

我正在尝试同时使用InheritableThreadLocalThreadPoolExecutor

这会导致因为ThreadPoolExecutor重用每个池的线程(毕竟它是一个池),这意味着InheritableThreadLocal无法按预期工作。现在这个问题对我来说显而易见,但是追踪它特别狡猾。

我使用InheritableThreadLocal,以便几个顶级进程中的每一个都有自己的数据库连接以及它产生的任何子进程。我不只是使用一个共享连接池,因为每个顶级进程在提交数据库和/或准备大量反复使用的PreparedStatements之前,会对其连接进行大量的多步骤工作。

我在这些顶级进程之间使用共享ThreadPoolExecutor,因为某些行为需要进行封闭。例如即使我可能有4个顶级进程在运行,但我一次只能有一个进程写入数据库(或者系统需要访问其他一些共享资源)。因此,我将让顶级流程创建Runnable并将其发送到共享ThreadPoolExecutor,以确保运行时不超过一个(或两个或三个)在整个系统中同时进行。

问题在于,因为ThreadPoolExecutor重用其池的线程,InheritableThreadLocal正在获取在该池中运行的原始值而不是顶层中的值将Runnable发送到ThreadPoolExecutor

的过程
  • 有没有办法强制ThreadPoolExecutor中的工作池使用创建Runnable的进程上下文中的InheritableThreadLocal值,而不是在重用线程池?

  • 或者,是否有ThreadPoolExecutor的任何实现在每次启动新的Runnable时创建一个新线程?出于我的目的,我只关心将同时运行的线程的数量设置为固定大小。

  • 人们有什么其他解决方案或建议可以让我完成上述内容吗?

(虽然我意识到我可以通过将类数据库连接从类传递到子线程到子线程来解决问题,就像某种社区自行车一样,我想避免这种情况。)

还有一个关于StackOverflow的问题InheritableThreadLocal and thread pools,它也解决了这个问题。但是,该问题的解决方案似乎是它对于InheritableThreadLocal来说是一个糟糕的用例,我不认为这适用于我的情况。

感谢任何想法。

4 个答案:

答案 0 :(得分:5)

使用InheritedThreadLocal几乎肯定是错的。可能你没有问过这个问题是否适合这个奇怪的工具。 首先,它可能非常容易泄漏,并且通常价值会在一些完全奇怪的线程中逃脱。

对于具有上下文的Runnable是关联的。 覆盖void execute(Runnable command)的公开ExecutorPool,并将Runnable包含在InheritedThreadLocal的首位,并带有您想要的值。

包装类看起来像

class WrappedRunnable extends Runnable{
  static final ThreadLocal<Ctx> context=new ThreadLocal<Ctx>();
  final Runnable target;
  final Ctx context;
  WrappedRunnable(Ctx context, Runnable target){...}

  public void run(){
    ctx.set(context);
    try{ 
      target.run();
    }finally{
      ctx.set(null);//or ctx.remove()
    }
  }
}
  

或者,是否有任何ThreadPoolExecutor的实现,每次启动一个新的Runnable时都会创建一个新的&gt;线程?出于我的目的,我只关心将&gt;同时运行的线程的数量设置为固定大小。

虽然从性能的角度来看真的很糟糕,但你可以实现自己的,基本上你只需execute(Runnable task)方法来生成新线程的Executor并启动它。

答案 1 :(得分:5)

为什么不使用java.util.concurrent.Semaphore而不是使用ThreadPoolExecutor来保护共享资源?你创建的子任务将在他们自己的线程中运行完成,但只有在从信号量获得许可之后,当然在完成时释放许可。

答案 2 :(得分:0)

我们之前遇到了同样的问题,我们通过编写ThreadLocalContextMigrator解决了这个问题,它基本上将线程本地上下文复制到将使用池中的线程执行的任务。执行任务时将收集更多上下文信息,完成任务后我们将其复制回来。

答案 3 :(得分:0)

为什么不将当前连接传递给主任务产生的任何子任务?也许是某种共享的Context对象?