keepAliveTime = 0 且 corePoolSize = 0 时 ThreadPoolExecutor 的行为
Behavior of ThreadPoolExecutor with keepAliveTime = 0 and corePoolSize = 0
将 ThreadPoolExecutor
的 keepAliveTime
和 corePoolSize
设置为 0
是否会为每个任务创建一个新的 Thread
?是否保证 Thread
不会被任何任务重复使用?
顺便说一句,我想将 maximumPoolSize
设置为 100
左右。我买不起无限数量的线程。如果我达到线程的限制(例如 100),我希望服务器回退到 'sychronous' 模式(无并行性)。见 ThreadPoolExecutor.CallerRunsPolicy
.
背景(如果您对我的动机感兴趣,请阅读):
我们有一个项目依赖于 ThreadLocals
的使用(例如,我们使用 Spring 及其 SecurityContextHolder
)。我们想并行调用 10 个后端系统。我们喜欢 ThreadPoolExecutor.CallerRunsPolicy
,它在线程池及其任务队列已满的情况下在调用者线程中运行可调用对象。这就是为什么我们要使用 ThreadPoolExecutor
。我无法将项目更改为不使用ThreadLocals
,请不要建议这样做。
我在想如何以最少的工作量完成它。 SecurityContextHolder
可以切换为使用 InheritableThreadLocal
而不是 ThreadLocal
。当创建子线程时,线程局部变量随后被传递给子线程。唯一的问题是如何让 ThreadPoolExecutor
为每个任务创建新的 Thread
。将其 keepAliveTime
和 corePoolSize
设置为 0
会起作用吗?我确定 none 的线程将被重新用于下一个任务吗?我可以忍受创建新 Threads
对性能造成的影响,因为每个并行任务都需要更多时间。
考虑的其他可能解决方案:
- 扩展
ThreadPoolExecutor
的 execute
方法并将 Runnable command
参数包装到一个不同的 Runnable
中,它会记住线程局部变量到它的最终字段中,然后在中初始化它们它的 run
方法在调用目标 command
之前。我认为这可能有效,但与我最初的问题所涉及的解决方案相比,需要维护的代码略多。
- 在异步方法的参数中传递局部线程。这使用起来更冗长。人们也可能忘记这样做,并且安全上下文将在异步任务之间共享:-(
- 扩展
ThreadPoolExecutor
的 beforeExecute
和 afterExecute
方法并使用反射复制线程局部变量。这需要大约 50 行丑陋的反射代码,我不确定它有多安全。
不,这行不通! ThreadPoolExecutor
将您的 Callable
/Runnable
包装到内部 Worker
对象中,并在其 runWorker()
方法中执行它。此方法的 Javadoc 说:
Main worker run loop. Repeatedly gets tasks from queue and executes them, while coping with a number of issues: ...
你也可以看一下这个方法的代码,它直到任务队列为空(或者发生一些坏事导致线程退出)才会退出。
因此将 keepAliveTime
设置为 0 不一定会在每个提交的任务上产生一个新线程。
您可能应该使用解决方案 3,因为 beforeExecute()
和 afterExecute()
方法正是用于处理 ThreadLocals。
或者,如果您坚持为每个任务创建新线程,您可以查看 Spring 的 SimpleAsyncTaskExecutor。它保证每个任务都有一个新线程,并允许您设置并发限制,即相当于 ThreadPoolExecutor#maxPoolSize
.
将 ThreadPoolExecutor
的 keepAliveTime
和 corePoolSize
设置为 0
是否会为每个任务创建一个新的 Thread
?是否保证 Thread
不会被任何任务重复使用?
顺便说一句,我想将 maximumPoolSize
设置为 100
左右。我买不起无限数量的线程。如果我达到线程的限制(例如 100),我希望服务器回退到 'sychronous' 模式(无并行性)。见 ThreadPoolExecutor.CallerRunsPolicy
.
背景(如果您对我的动机感兴趣,请阅读):
我们有一个项目依赖于 ThreadLocals
的使用(例如,我们使用 Spring 及其 SecurityContextHolder
)。我们想并行调用 10 个后端系统。我们喜欢 ThreadPoolExecutor.CallerRunsPolicy
,它在线程池及其任务队列已满的情况下在调用者线程中运行可调用对象。这就是为什么我们要使用 ThreadPoolExecutor
。我无法将项目更改为不使用ThreadLocals
,请不要建议这样做。
我在想如何以最少的工作量完成它。 SecurityContextHolder
可以切换为使用 InheritableThreadLocal
而不是 ThreadLocal
。当创建子线程时,线程局部变量随后被传递给子线程。唯一的问题是如何让 ThreadPoolExecutor
为每个任务创建新的 Thread
。将其 keepAliveTime
和 corePoolSize
设置为 0
会起作用吗?我确定 none 的线程将被重新用于下一个任务吗?我可以忍受创建新 Threads
对性能造成的影响,因为每个并行任务都需要更多时间。
考虑的其他可能解决方案:
- 扩展
ThreadPoolExecutor
的execute
方法并将Runnable command
参数包装到一个不同的Runnable
中,它会记住线程局部变量到它的最终字段中,然后在中初始化它们它的run
方法在调用目标command
之前。我认为这可能有效,但与我最初的问题所涉及的解决方案相比,需要维护的代码略多。 - 在异步方法的参数中传递局部线程。这使用起来更冗长。人们也可能忘记这样做,并且安全上下文将在异步任务之间共享:-(
- 扩展
ThreadPoolExecutor
的beforeExecute
和afterExecute
方法并使用反射复制线程局部变量。这需要大约 50 行丑陋的反射代码,我不确定它有多安全。
不,这行不通! ThreadPoolExecutor
将您的 Callable
/Runnable
包装到内部 Worker
对象中,并在其 runWorker()
方法中执行它。此方法的 Javadoc 说:
Main worker run loop. Repeatedly gets tasks from queue and executes them, while coping with a number of issues: ...
你也可以看一下这个方法的代码,它直到任务队列为空(或者发生一些坏事导致线程退出)才会退出。
因此将 keepAliveTime
设置为 0 不一定会在每个提交的任务上产生一个新线程。
您可能应该使用解决方案 3,因为 beforeExecute()
和 afterExecute()
方法正是用于处理 ThreadLocals。
或者,如果您坚持为每个任务创建新线程,您可以查看 Spring 的 SimpleAsyncTaskExecutor。它保证每个任务都有一个新线程,并允许您设置并发限制,即相当于 ThreadPoolExecutor#maxPoolSize
.