Java 将InheritableThreadLocal与ThreadPoolExecutor一起使用--或--不使用';不要重复使用线程

Java 将InheritableThreadLocal与ThreadPoolExecutor一起使用--或--不使用';不要重复使用线程,java,multithreading,executorservice,Java,Multithreading,Executorservice,我试图同时使用InheritableThreadLocal和ThreadPoolExecutor 这是因为ThreadPoolExecutor为每个池(毕竟它是一个池)重用线程,这意味着InheritableThreadLocal无法按预期工作。现在这个问题对我来说似乎很明显,但追查起来特别麻烦 我使用了InheritableThreadLocal,以便几个顶级进程中的每个进程都有自己的数据库连接,以及它产生的任何子进程。我不只是使用一个共享连接池,因为在提交到数据库和/或准备大量反复使用的Pr

我试图同时使用
InheritableThreadLocal
ThreadPoolExecutor

这是因为
ThreadPoolExecutor
为每个池(毕竟它是一个池)重用线程,这意味着
InheritableThreadLocal
无法按预期工作。现在这个问题对我来说似乎很明显,但追查起来特别麻烦

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

我在这些顶级进程之间使用共享的
ThreadPoolExecutor
,因为有些行为需要被限制。e、 g.尽管我可能有4个顶级进程在运行,但一次只能有一个进程写入数据库(或者系统需要关闭其他共享资源)。因此,我将让顶级进程创建一个
Runnable
,并将其发送给共享
ThreadPoolExecutor
,以确保在整个系统中同时运行的线程不超过一个(或两个或三个,视情况而定)

问题在于,由于
ThreadPoolExecutor
为池重用其线程,因此
InheritableThreadLocal
将拾取在该池中运行的原始值,而不是将Runnable发送给
ThreadPoolExecutor
的顶级进程中的值

  • 是否有任何方法可以强制
    ThreadPoolExecutor
    中的工作线程池使用创建可运行线程的进程上下文中的
    InheritableThreadLocal
    值,而不是重用线程池的上下文中的值

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

  • 有没有其他的解决方案或建议让我完成我上面所描述的

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

前面有一个关于StackOverflow的问题,也解决了这个问题。然而,这个问题的解决方案似乎是,对于InheritableThreadLocal来说,它是一个糟糕的用例,我认为这不适用于我的情况


谢谢你的建议。

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

使用
InheritedThreadLocal
几乎肯定是错误的。也许你不会问这个问题,如果你能适应这个奇怪的工具。 首先也是最重要的一点是,它极易泄漏,并且经常会在一些完全奇怪的线程中漏出值

至于可运行的,则与上下文关联。 覆盖
ExecutorPool
的public
void execute(Runnable命令)
,并使用一些上下文包装
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()
    }
  }
}
类WrappedRunnable扩展了Runnable{
静态最终ThreadLocal上下文=新ThreadLocal();
最终可运行目标;
最终Ctx上下文;
WrappedRunnable(Ctx上下文,可运行目标){…}
公开募捐{
ctx.set(上下文);
试试{
target.run();
}最后{
ctx.set(null);//或ctx.remove()
}
}
}
或者,是否有ThreadPoolExecutor的任何实现在每次启动新的Runnable时创建一个新的>线程?出于我的目的,我只关心将>个同时运行的线程设置为固定大小


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

为什么不将当前连接传递给主任务生成的任何子任务呢?可能是某种共享上下文对象?

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

您能提供更多关于如何做到这一点的详细信息吗?
ThreadLocalContextMigrator.java
是否公开提供?您为什么说InheritableThreadLocal容易泄漏?我在谷歌上搜索,没有看到任何关于它导致内存泄漏的文章或例子。你能告诉我一些来源吗?@JeffGoldberg,嗯,我很清楚它是如何实现的,我做了很多基础设施/中间件。ThreadLocal容易泄漏,而继承的版本非常容易泄漏。实际上,无论出于何种原因,您都无法控制何时生成新线程(JDK本身就是这样做的)。一个长时间运行的线程将泄漏该值及其类加载器,并且无法阻止那个遥远的线程持有对其他无用对象的引用
childValue
可以修复该情况,但它是在保存它的线程中调用的,并且新的目标线程是未知的