将 InheritableThreadLocal 与 ThreadPoolExecutor 一起使用 -- 或者 -- 不重用线程的 ThreadPoolExecutor

2022-09-02 10:50:06

我正在尝试同时使用和.InheritableThreadLocalThreadPoolExecutor

这之所以崩溃,是因为重用每个池的线程(毕竟它是一个池),这意味着 不能按预期工作。现在,这个问题对我来说似乎很明显,但追踪起来尤其棘手。ThreadPoolExecutorInheritableThreadLocal

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

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

问题在于,由于 the 重用其线程用于池,因此 正在选取在该池中运行的原始值,而不是在将 Runnable 发送到 的顶级进程中的值。ThreadPoolExecutorInheritableThreadLocalThreadPoolExecutor

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

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

  • 人们有没有其他解决方案或建议让我完成上面描述的事情?

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

前面有一个关于StackOverflow,InheritableThreadLocal和线程池的问题,也可以解决这个问题。但是,这个问题的解决方案似乎是,对于 InheritableThreadLocal 来说,这是一个糟糕的用例,我认为这不适用于我的情况。

感谢您的任何想法。


答案 1

使用几乎肯定是错误的。如果你可以安装这个奇怪的工具,你可能不会问这个问题。首先,它非常容易泄漏,并且经常在一些完全奇怪的线程中逃脱值。InheritedThreadLocal

至于 Runnable 与上下文相关联。覆盖 的 public 并包装一些上下文,从 中首先携带您想要的值。void execute(Runnable command)ExecutorPoolRunnableInheritedThreadLocal

包装类应如下所示

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 时都会创建一个新的>ththread?出于我的目的,我只关心将>数量的同时运行的线程控制到固定大小。

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


答案 2

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