ThreadPoolTask​​Executor Spring 的默认队列大小是多少?

What's Spring's default queue size with a ThreadPoolTaskExecutor?

我正在使用 Spring 4.3.8.RELEASE 和 Java 7. 我想创建一个线程池来执行任务,所以我在 Spring contxet

<bean id="myThreadFactory" class="org.springframework.scheduling.concurrent.CustomizableThreadFactory">
    <constructor-arg value="mythread-"/>
</bean>
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="threadFactory" ref="myThreadFactory"/>
    <property name="corePoolSize" value="10" />
    <property name="maxPoolSize" value="50" />
</bean>

为了不破坏机器 CPU 的使用,我想限制系统中可以存在的并发线程数量(我假设这就是 maxPOolSize 所做的)。但我不希望任务被丢弃。如果我向 taskPoolExecutor 添加超过 50 个任务,编号 51 会发生什么情况?更重要的是,在开始删除之前可以添加的默认任务数是多少?

设置 maxPoolSize 隐式允许删除任务。 但是,默认的队列容量是 Integer.MAX_VALUE,实际上是无穷大。

需要注意的是 ThreadPoolTaskExecutor 在下面使用了一个 ThreadPoolExecutor,它有一些不寻常的排队方法,在 the docs:

中有描述

If corePoolSize or more threads are running, the Executor always prefers queuing a request rather than adding a new thread.

这意味着 maxPoolSize 仅在队列已满时才相关,否则线程数永远不会超过 corePoolSize。 例如,如果我们提交任务 永远不会完成 到线程池:

  • corePoolSize 个提交将分别启动一个新线程;
  • 之后,所有提交都进入队列;
  • 如果队列有限且容量耗尽,则每次提交都会启动一个新线程,直到池中有 maxPoolSize 个线程;
  • 当池和队列都已满时,新的提交将被拒绝。

如果执行了许多 @Async 任务,请注意使用默认值 ThreadPoolTaskExecutor

从 Spring Boot 2.1 开始,默认的 ThreadPoolTaskExecutor 核心大小为八个线程。尽管最大池大小仍然是无穷大¹并且理论上可以创建新线程,但该池的队列大小也是无穷大¹。

队列大小可能永远不会达到并且永远不会创建新线程。

如果使用 @Async,至少设置队列大小:

spring.task.execution.pool.queue-capacity=16

¹ 无穷大如 2147483647 a.k.a。 Integer.MAX_VALUE

请注意 ThreadpoolTask​​Exectuor 中可用的不同队列配置。

Any BlockingQueue may be used to transfer and hold submitted tasks. The use of this queue interacts with pool sizing: If fewer than corePoolSize threads are running, the Executor always prefers adding a new thread rather than queuing. If corePoolSize or more threads are running, the Executor always prefers queuing a request rather than adding a new thread. If a request cannot be queued, a new thread is created unless this would exceed maximumPoolSize, in which case, the task will be rejected.

排队的一般策略有3种:

  1. 直接切换。工作队列的一个很好的默认选择是 SynchronousQueue,它将任务交给线程而不用其他方式持有它们。在这里,如果没有线程立即可用于 运行,则尝试将任务排队将失败,因此将构造一个新线程。该策略在处理可能具有内部依赖性的请求集时避免了锁定。直接切换通常需要无限的 maximumPoolSizes 以避免拒绝新提交的任务。这反过来又承认当命令继续以平均比处理速度更快的速度到达时线程无限制增长的可能性。
  2. 无限队列。使用无界队列(例如没有预定义容量的 LinkedBlockingQueue)将导致新任务在所有 corePoolSize 线程都忙时在队列中等待。因此,将永远不会创建超过 corePoolSize 个线程。 (因此 maximumPoolSize 的值没有任何影响。)当每个任务完全独立于其他任务时,这可能是合适的,因此任务不会影响彼此的执行;例如,在网页服务器中。虽然这种排队方式可用于平滑请求的瞬时爆发,但它承认当命令继续以平均比处理速度更快的速度到达时,工作队列无限制增长的可能性。
  3. 有界队列。有界队列(例如,ArrayBlockingQueue)在与有限的 maximumPoolSizes 一起使用时有助于防止资源耗尽,但可以更难调整和控制。队列大小和最大池大小可以相互权衡:使用大队列和小池可以最大限度地减少 CPU 使用率、OS 资源和上下文切换开销,但会导致人为的低吞吐量。如果任务频繁阻塞(例如,如果它们被 I/O 绑定),系统可能能够为比您允许的更多的线程安排时间。使用小队列通常需要更大的池大小,这会使 CPUs 更忙,但可能会遇到无法接受的调度开销,这也会降低吞吐量。 (我们在多次测试后实施了这种方法,通过核心池和最大池大小调整队列大小)。

REF : https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html