java Spring 的 ThreadPoolTask​​Executor 的池大小问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32157817/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 19:44:53  来源:igfitidea点击:

Problems with pool size of Spring's ThreadPoolTaskExecutor

javamultithreadingspringthreadpoolthreadpoolexecutor

提问by baymon

I'm doing some load tests agains my Spring application and now I'm a little bit confused about the configuration of the ThreadPoolTaskExecutor.

我正在对我的 Spring 应用程序进行一些负载测试,现在我对ThreadPoolTaskExecutor.

The documentation of the internally used ThreadPoolExecutordescribes the corePoolSizeas "the number of threads to keep in the pool, even if they are idle, [...]" and maximumPoolSizeas "the maximum number of threads to allow in the pool".

内部使用的文档将其ThreadPoolExecutor描述corePoolSize为“池中保留的线程数,即使它们处于空闲状态,[...]”和maximumPoolSize“池中允许的最大线程数”。

That obviously means that the maximumPoolSizelimits the number of thread in the pool. But instead the limit seems the be set by the corePoolSize. Actually I configured just the corePoolSizewith 100an let the maximumPoolSizeunconfigured (that means the default value is used: Integer.MAX_VALUE= 2147483647).

这显然意味着maximumPoolSize限制了池中的线程数。但相反,限制似乎由corePoolSize. 实际上我配置只是corePoolSize100一个让maximumPoolSize未配置(即装置的默认值用于:Integer.MAX_VALUE= 2147483647)。

When I run the load test I can see (by reviewing the logs), that the executed worker thread are numbered from worker-1to worker-100. So in this case the thread pool size is limited by corePoolSize. Even if I set maximumPoolSizeto 200or 300, the result is exactly the same.

当我运行负载测试时,我可以看到(通过查看日志),执行的工作线程编号从worker-1worker-100。因此,在这种情况下,线程池大小受corePoolSize. 即使我设置maximumPoolSize200300,结果也完全相同。

Why the value of maximumPoolSizehas no affect in my case?

为什么 的值对maximumPoolSize我的情况没有影响?

@Bean
public TaskExecutor taskExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(100);
    taskExecutor.setThreadNamePrefix("worker-");
    return taskExecutor;
}

SOLUTION

解决方案

I've found the solution in the documentation: "If there are more than corePoolSize but less than maximumPoolSize threads running, a new thread will be created only if the queue is full". The default queue size is Integer.MAX_VALUE. If I limit the queue, everything works fine.

我在文档中找到了解决方案:“如果运行的线程数超过 corePoolSize 但少于 maximumPoolSize,则仅当队列已满时才会创建新线程”。默认队列大小为Integer.MAX_VALUE. 如果我限制队列,一切正常。

回答by Somnath De

I have done some testing on ThreadPoolTaskExecutorand there is three things that you have to understand:

我已经做了一些测试ThreadPoolTaskExecutor,你必须了解三件事:

  • corePoolSize
  • queueCapacity
  • maxPoolSize
  • 核心池大小
  • 队列容量
  • 最大池大小

When you start the process there is no threads in the pool. Each time a task comes one new executor thread will be created to handle this new load as long the corePoolSizeis not reached. When the corePoolSizeis reached the next task will be shift to the queue and wait for a free executor thread. If the load is too high and queueCapacityis full, the new executor threads will be created unless the maxPoolSizeis reached. These additional threads will expire as soon as the queue is empty. If the corePoolSizeis exhausted, queueCapacityis full and maxPoolSizeis also reached then the new submitteds tasks will be rejected and called will get an exception.

当您启动进程时,池中没有线程。每次任务到来时,都会创建一个新的执行程序线程来处理这个新的负载,只要corePoolSize没有达到。当corePoolSize到达时,下一个任务将被转移到队列中并等待一个空闲的执行器线程。如果负载过高且queueCapacity已满,则除非maxPoolSize达到 ,否则将创建新的执行器线程。一旦队列为空,这些额外的线程就会过期。如果corePoolSize已用完,queueCapacity已满并且maxPoolSize也已达到,则新提交的任务将被拒绝并被调用将获得异常。

You have not mentioned the queueCapacityof your configuration so it might be set to highest integer number and thus maxPoolSizeis never getting triggered. Try with small corePoolSizeand queueCapacityand you will observe the desired result.

你没有提到queueCapacity你的配置,所以它可能被设置为最高整数,因此maxPoolSize永远不会被触发。尝试使用 smallcorePoolSize并且queueCapacity您将观察到所需的结果。

回答by luboskrnac

If you have 100 threads in a pool and you are executing CPU bound code on 4 physical CPU cores, most of your core threads are idle in the pool waiting to be re-used. That is probably why you don't see more than worker-100.

如果池中有 100 个线程,并且您正在 4 个物理 CPU 内核上执行 CPU 绑定代码,那么大多数内核线程在池中处于空闲状态,等待重新使用。这可能就是为什么你看不到超过 worker-100 的原因。

You didn't show us code you are executing in workers, therefore I assume it is not I/O bound. If it would be I/O bound code and 100 of your core threads would be occupied by waiting for blocking I/O operations to finish, ThreadPoolExecutorwould need to create additional workers.

您没有向我们展示您在工作人员中执行的代码,因此我认为它不受 I/O 限制。如果它是 I/O 绑定代码并且 100 个核心线程将被等待阻塞 I/O 操作完成占用,则ThreadPoolExecutor需要创建额外的工作线程。

Try it with corePoolSizelower than number of cores on your machine to confirm. Another option is to put Thread.sleep(1000)into your worker code and observe how your workers count will be raising.

尝试使用corePoolSize低于您机器上的核心数进行确认。另一种选择是放入Thread.sleep(1000)您的工人代码并观察您的工人数量将如何增加。

EDIT:

编辑:

You suggested to use SimpleAsyncTaskExecutorin comment. Notice this section of Spring Framework docs:

您建议SimpleAsyncTaskExecutor在评论中使用。请注意Spring Framework 文档的这一部分:

SimpleAsyncTaskExecutor This implementation does not reuse any threads, rather it starts up a new thread for each invocation. However, it does support a concurrency limit which will block any invocations that are over the limit until a slot has been freed up. If you are looking for true pooling, see the discussions of SimpleThreadPoolTaskExecutor and ThreadPoolTaskExecutor below.

SimpleAsyncTaskExecutor 此实现不重用任何线程,而是为每次调用启动一个新线程。但是,它确实支持并发限制,该限制将阻止任何超出限制的调用,直到一个插槽被释放为止。如果您正在寻找真正的池化,请参阅下面关于 SimpleThreadPoolTask​​Executor 和 ThreadPoolTask​​Executor 的讨论。

So with SimpleAsyncTaskExecutoryou don't have pooling at all and a lot of resources (CPU cycles included) are wasted on creation and deletion of Threadobjects, which may be quite expensive operation.

因此,SimpleAsyncTaskExecutor您根本没有池化,并且在创建和删除Thread对象上浪费了大量资源(包括 CPU 周期),这可能是非常昂贵的操作。

So SimpleAsyncTaskExecutorexecutor type does more harm than good to your load testing. If you want to have more workers, use more machines. It's naive to use only one machine if you want to have accurate load testing.

因此,SimpleAsyncTaskExecutor执行器类型对您的负载测试弊大于利。如果您想拥有更多工人,请使用更多机器。如果您想进行准确的负载测试,只使用一台机器是很幼稚的。