我正在尝试同时使用InheritableThreadLocal
和ThreadPoolExecutor
。
这是因为ThreadPoolExecutor
为每个池重用线程(毕竟它是一个池),这意味着InheritableThreadLocal
不能按预期工作。现在,这个问题对我来说似乎很明显,但追踪起来尤其困难。
我使用InheritableThreadLocal
,这样几个顶级进程中的每一个都有自己的数据库连接,以及它产生的任何子进程。我不只是使用一个共享连接池,因为在提交到数据库和/或准备大量重复使用的PreparedStatements之前,每个顶级进程都会对其连接进行大量的多步骤工作。
我在这些顶级进程之间使用共享的ThreadPoolExecutor
,因为有某些行为需要门控。例如,尽管我可能有4个顶级进程在运行,但一次只能有任何一个进程写入数据库(或者系统需要访问其他共享资源)。因此,我将让顶级进程创建一个Runnable
,并将其发送到共享的ThreadPoolExecutor
,以确保在整个系统中同时运行的进程不超过一个(或两个或三个,视情况而定)。
问题是,因为ThreadPoolExecutor
为池重用其线程,所以InheritableThreadLocal
正在获取在该池中运行的原始值,而不是将Runnable发送到ThreadPoolExecutor
的顶级进程中的值。
-
是否有任何方法可以强制
ThreadPoolExecutor
中的工作池使用创建Runnable的进程上下文中的InheritableThreadLocal
值,而不是在重用线程池的上下文中? -
或者,
ThreadPoolExecutor
是否有任何实现可以在每次启动新的Runnable时创建一个新线程?出于我的目的,我只关心将同时运行的线程数量选通到固定大小。 -
有没有其他解决方案或建议可以让我完成我上面描述的内容?
(虽然我意识到我可以像某种社区自行车一样,通过将数据库连接从一个类传递到另一个类再传递到子线程来解决这个问题,但我希望避免这种情况。)
之前有一个关于StackOverflow、InheritableThreadLocal和线程池的问题,也解决了这个问题。然而,这个问题的解决方案似乎是InheritableThreadLocal的一个糟糕的用例,我认为这不适用于我的情况。
谢谢你的任何想法。
使用InheritedThreadLocal
几乎肯定是错误的。如果你能安装这个奇怪的工具,你可能不会问这个问题。首先也是最重要的一点是,它非常容易泄漏,而且值经常在一些完全奇怪的线程中逃逸。
至于与上下文关联的Runnable。重写ExecutorPool
的公共void execute(Runnable command)
,并将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时创建一个新的>线程?出于我的目的,我只关心将>同时运行的线程数选通到固定大小。
虽然从性能的角度来看确实很糟糕,但您可以实现自己的方法,基本上只需要Executor
的execute(Runnable task)
方法就可以产生新线程并启动它
与其使用ThreadPoolExecutor来保护共享资源,为什么不使用java.util.concurrent.Semaphore
呢?您创建的子任务将在它们自己的线程中运行到完成,但只有在从信号量获得许可之后,当然在完成后释放许可。
我们之前也遇到过同样的问题,我们通过编写ThreadLocalContextMigrator解决了这个问题,它基本上将线程本地上下文复制到将使用池中的线程执行的任务中。任务在执行时将收集更多的上下文信息,并在任务完成后将其复制回来。
为什么不将当前连接传递给主任务生成的任何子任务?也许是某种共享上下文对象?