将ThreadPoolExecutor
' keepAliveTime
和corePoolSize
设置为0
是否会为每项任务创建新的Thread
?是否保证任何Thread
都不会被重用于任何任务?
BTW我想将maximumPoolSize
设置为100
左右。我无法承受无限量的线程。如果我达到了线程的限制(例如100),我希望服务器回退到“同步”状态。模式(没有并行性)。请参阅ThreadPoolExecutor.CallerRunsPolicy
。
背景(仅在您对我的动机感兴趣时阅读):
我们有一个项目依赖于ThreadLocals
的使用(例如我们使用Spring及其SecurityContextHolder
)。我们希望并行调用10个后端系统。我们喜欢ThreadPoolExecutor.CallerRunsPolicy
,它在调用程序线程中运行可调用,以防线程池及其任务队列已满。这就是我们希望使用ThreadPoolExecutor
的原因。我无法更改项目不使用ThreadLocals
,请不要建议这样做。
我正在考虑如何以最少的工作量去做。可以将SecurityContextHolder
切换为使用InheritableThreadLocal
而不是ThreadLocal
。然后,在创建子线程时,将线程局部变量传递给子线程。唯一的问题是如何让ThreadPoolExecutor
为每项任务创建新的Thread
。将其keepAliveTime
和corePoolSize
设置为0
会有效吗?我确定没有一个线程会被重用于下一个任务吗?我可以接受创建新Threads
的性能影响,因为并行任务每个都花费更多时间。
考虑了其他可能的解决方案:
ThreadPoolExecutor
的{{1}}方法,并将execute
参数包装到另一个Runnable command
中,该Runnable
会将线程本地记录到其最终字段中,然后将其初始化在调用目标run
之前的command
方法中。我认为这可能有用,但是维护的代码比我原来的问题解决方案要多得多。ThreadPoolExecutor
的{{1}}和beforeExecute
方法,并使用反射复制线程本地。这需要大约50行丑陋的反射代码,我不确定它是多么安全。答案 0 :(得分:0)
不,这不行! ThreadPoolExecutor
将您的Callable
/ Runnable
包装到内部Worker
对象中,并以runWorker()
方式执行。这种方法的Javadoc说:
主要工作人员运行循环。从队列中反复获取任务并执行它们,同时处理许多问题:...
您还可以查看此方法的代码,并看到它在任务队列为空(或发生导致线程退出的不良事件)之前不会退出。
因此,将keepAliveTime
设置为0不一定会在每个提交的任务上产生新线程。
你可能应该使用你的解决方案3,因为beforeExecute()
和afterExecute()
方法正好用于处理ThreadLocals。
或者,如果您坚持为每项任务设置新主题,您可以查看Spring的SimpleAsyncTaskExecutor。它保证为每个任务创建一个新线程,并允许您设置并发限制,即相当于ThreadPoolExecutor#maxPoolSize
。