ThreadPoolTaskExecutor corePoolSize vs. maxPoolSize – ThreadPoolTaskExecutor corePoolSize与maxPoolSize的比较

最后修改: 2020年 2月 29日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

The Spring ThreadPoolTaskExecutor is a JavaBean that provides an abstraction around a java.util.concurrent.ThreadPoolExecutor instance and exposes it as a Spring org.springframework.core.task.TaskExecutor. Further, it is highly configurable through the properties of corePoolSize, maxPoolSize, queueCapacity, allowCoreThreadTimeOut and keepAliveSeconds. In this tutorial, we’ll look at the corePoolSize and maxPoolSize properties.

Spring ThreadPoolTaskExecutor是一个JavaBean,它围绕java.util.concurrent.ThreadPoolExecutor实例提供了一个抽象,并将其作为Spring org.springframework.core.task.TaskExecutor公开。此外,通过corePoolSize、maxPoolSize、queueCapacity、allowCoreThreadTimeOutkeepAliveSeconds的属性,它是高度可配置的。

2. corePoolSize vs. maxPoolSize

corePoolSize vs maxPoolSize 2.

Users new to this abstraction may easily get confused about the difference in the two configuration properties. Therefore, let’s look at each independently.

刚接触这个抽象概念的用户可能很容易对这两个配置属性的区别感到困惑。因此,让我们分别看一下。

2.1. corePoolSize

2.1corePoolSize

The corePoolSize is the minimum number of workers to keep alive without timing out. It is a configurable property of ThreadPoolTaskExecutor. However, the ThreadPoolTaskExecutor abstraction delegates setting this value to the underlying java.util.concurrent.ThreadPoolExecutor. To clarify, all threads may time out — effectively setting the value of corePoolSize to zero if we’ve set allowCoreThreadTimeOut to true.

corePoolSize是在不超时的情况下保持存活的最小数量的工作者。它是ThreadPoolTaskExecutor的一个可配置属性。然而,ThreadPoolTaskExecutor抽象将此值的设置委托给底层的java.util.concurrent.ThreadPoolExecutor。为了澄清,所有线程都可能超时 – 如果我们将allowCoreThreadTimeOut设置为true,那么实际上将corePoolSize的值设置为零。

2.2. maxPoolSize

2.2. maxPoolSize

In contrast, the maxPoolSize defines the maximum number of threads that can ever be created. Similarly, the maxPoolSize property of ThreadPoolTaskExecutor also delegates its value to the underlying java.util.concurrent.ThreadPoolExecutor. To clarify, maxPoolSize depends on queueCapacity in that ThreadPoolTaskExecutor will only create a new thread if the number of items in its queue exceeds queueCapacity.

相反,maxPoolSize定义了可以创建的最大线程数。同样,ThreadPoolTaskExecutormaxPoolSize属性也将其值委托给底层的java.util.concurrent.ThreadPoolExecutor。澄清一下,maxPoolSize取决于queueCapacity,因为ThreadPoolTaskExecutor只会在其队列中的项目数超过queueCapacity时创建一个新线程。

3. So What’s the Difference?

3.那么有什么区别呢?

The difference between corePoolSize and maxPoolSize may seem evident. However, there are some subtleties regarding their behavior.

corePoolSizemaxPoolSize之间的区别可能看起来很明显。然而,关于它们的行为有一些微妙之处。

When we submit a new task to the ThreadPoolTaskExecutor, it creates a new thread if fewer than corePoolSize threads are running, even if there are idle threads in the pool, or if fewer than maxPoolSize threads are running and the queue defined by queueCapacity is full.

当我们向ThreadPoolTaskExecutor提交一个新任务时,如果少于corePoolSize的线程正在运行,即使池中有空闲的线程,或者少于maxPoolSize的线程正在运行,并且由queueCapacity定义的队列已满,它将创建一个新线程。

Next, let’s look at some code to see examples of when each property springs into action.

接下来,让我们看看一些代码,看看每个属性何时发挥作用的例子。

4. Examples

4.实例

Firstly, let’s say we have a method that executes new threads, from the ThreadPoolTaskExecutor, named startThreads:

首先,假设我们有一个执行新线程的方法,来自ThreadPoolTaskExecutor,名为startThreads

public void startThreads(ThreadPoolTaskExecutor taskExecutor, CountDownLatch countDownLatch, 
  int numThreads) {
    for (int i = 0; i < numThreads; i++) {
        taskExecutor.execute(() -> {
            try {
                Thread.sleep(100L * ThreadLocalRandom.current().nextLong(1, 10));
                countDownLatch.countDown();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
    }
}

Let’s test the default configuration of ThreadPoolTaskExecutor, which defines a corePoolSize of one thread, an unbounded maxPoolSize, and an unbounded queueCapacity. As a result, we expect that no matter how many tasks we start, we’ll only have one thread running:

让我们测试一下ThreadPoolTaskExecutor的默认配置,它定义了一个线程的corePoolSize,一个无界的maxPoolSize和一个无界的queueCapacity。因此,我们期望无论我们启动多少个任务,我们都只有一个线程在运行。

@Test
public void whenUsingDefaults_thenSingleThread() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.afterPropertiesSet();

    CountDownLatch countDownLatch = new CountDownLatch(10);
    this.startThreads(taskExecutor, countDownLatch, 10);

    while (countDownLatch.getCount() > 0) {
        Assert.assertEquals(1, taskExecutor.getPoolSize());
    }
}

Now, let’s alter the corePoolSize to a max of five threads and ensure it behaves as advertised. As a result, we expect five threads to be started no matter the number of tasks submitted to the ThreadPoolTaskExecutor:

现在,让我们把corePoolSize改为最多5个线程,并确保它的行为符合广告要求。因此,无论提交给ThreadPoolTaskExecutor的任务数量如何,我们都希望有五个线程被启动。

@Test
public void whenCorePoolSizeFive_thenFiveThreads() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(5);
    taskExecutor.afterPropertiesSet();

    CountDownLatch countDownLatch = new CountDownLatch(10);
    this.startThreads(taskExecutor, countDownLatch, 10);

    while (countDownLatch.getCount() > 0) {
        Assert.assertEquals(5, taskExecutor.getPoolSize());
    }
}

Similarly, we can increment the maxPoolSize to ten while leaving the corePoolSize at five. As a result, we expect to start only five threads. To clarify, only five threads start because the queueCapacity is still unbounded:

同样,我们可以将maxPoolSize增加到10,而将corePoolSize保持在5。结果是,我们预计只启动五个线程。要说明的是,只启动五个线程是因为queueCapacity仍然是无界的。

@Test
public void whenCorePoolSizeFiveAndMaxPoolSizeTen_thenFiveThreads() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(5);
    taskExecutor.setMaxPoolSize(10);
    taskExecutor.afterPropertiesSet();

    CountDownLatch countDownLatch = new CountDownLatch(10);
    this.startThreads(taskExecutor, countDownLatch, 10);

    while (countDownLatch.getCount() > 0) {
        Assert.assertEquals(5, taskExecutor.getPoolSize());
    }
}

Further, we’ll now repeat the previous test but increment the queueCapacity to ten and start twenty threads. Therefore, we now expect to start ten threads in total:

此外,我们现在将重复之前的测试,但将queueCapacity增加到10,并启动20个线程。因此,我们现在期望总共启动十个线程。

@Test
public void whenCorePoolSizeFiveAndMaxPoolSizeTenAndQueueCapacityTen_thenTenThreads() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(5);
    taskExecutor.setMaxPoolSize(10);
    taskExecutor.setQueueCapacity(10);
    taskExecutor.afterPropertiesSet();

    CountDownLatch countDownLatch = new CountDownLatch(20);
    this.startThreads(taskExecutor, countDownLatch, 20);

    while (countDownLatch.getCount() > 0) {
        Assert.assertEquals(10, taskExecutor.getPoolSize());
    }
}

Likewise, if we had set the queueCapactity to zero and only started ten tasks, we’d also have ten threads in our ThreadPoolTaskExecutor.

同样,如果我们将queueCapactity设置为零,并且只启动了十个任务,那么我们的ThreadPoolTaskExecutor中也会有十个线程。

5. Conclusion

5.总结

ThreadPoolTaskExecutor is a powerful abstraction around a java.util.concurrent.ThreadPoolExecutor, providing options for configuring the corePoolSize, maxPoolSize, and queueCapacity. In this tutorial, we looked at the corePoolSize and maxPoolSize properties, as well as how maxPoolSize works in tandem with queueCapacity, allowing us to easily create thread pools for any use case.

ThreadPoolTaskExecutor是围绕java.util.concurrent.ThreadPoolExecutor的一个强大的抽象,为配置corePoolSizemaxPoolSizequeueCapacity提供选项。在本教程中,我们研究了corePoolSizemaxPoolSize属性,以及maxPoolSize如何与queueCapacity协同工作,使我们能够为任何用例轻松创建线程池。

As always, you can find the code available over on Github.

一如既往,你可以在Github上找到可用的代码