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

Jef*_*erg 17 java multithreading executorservice

我想用两个InheritableThreadLocal和一个ThreadPoolExecutor.

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

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

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

问题在于,因为ThreadPoolExecutor重用了池中的线程,所以它将获取在该池中 InheritableThreadLocal运行的原始值,而不是将Runnable发送到的顶级进程中的值ThreadPoolExecutor.

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

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

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

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

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

谢谢你的任何想法.

bes*_*sss 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()
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

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

虽然从性能的角度来看真正的坏,你可以实现你自己的,基本上你只需要execute(Runnable task)在方法Executor会派生新的线程并启动它.


for*_*two 5

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