侧边栏壁纸
博主头像
翻斗

开始一件事最好是昨天,其次是现在

  • 累计撰写 44 篇文章
  • 累计创建 42 个标签
  • 累计收到 2 条评论

ThreadPoolTaskExecutor中的corePoolSize和maxPoolSize

翻斗
2020-04-05 / 0 评论 / 0 点赞 / 646 阅读 / 3,984 字

Baeldung 翻译系列之Java并发基础:

Java中的concurrent包
Java中的Synchronized关键字
Future介绍
ThreadLocal介绍
Java线程的生命周期
如何杀掉一个Java线程
Java中的线程池介绍
实现Runnable接口还是继承Thread类
Java中的wait和notify方法
Runnable vs Callable
wait和sleep的区别
Thread.join方法介绍
Java中使用锁对象
ThreadPoolTaskExecutor中的corePoolSize和maxPoolSize
Java中的异步编程

1. 概述

SpringThreadPoolTaskExecutor是一个JavaBean,它提供了对java.util.concurrent.ThreadPoolExecutor实例的抽象,并将其作为Springorg.springframework.core.task.TaskExecutor暴露出来。此外,通过corePoolSizemaxPoolSizequeueCapacityallowCoreThreadTimeOutkeepAliveSeconds等属性,它具有高度可配置性。在本教程中,我们将重点关注corePoolSizemaxPoolSize属性。

2. corePoolSize vs. maxPoolSize

对于这个抽象的新用户可能会对这两个配置属性的区别感到困惑。因此,让我们分别看一下它们。

2.1. corePoolSize

corePoolSize是保持活动状态的最小工作线程数。它是ThreadPoolTaskExecutor的可配置属性。然而,ThreadPoolTaskExecutor的抽象将设置此值的工作委托给底层的java.util.concurrent.ThreadPoolExecutor。需要注意的是,如果我们将allowCoreThreadTimeOut设置为true,所有线程都可能超时,从而将corePoolSize的值设置为零。

2.2. maxPoolSize

相比之下,maxPoolSize定义了可以创建的最大线程数。同样,ThreadPoolTaskExecutormaxPoolSize属性也将其值委托给底层的java.util.concurrent.ThreadPoolExecutor。需要注意的是,maxPoolSize依赖于queueCapacity,只有当队列中的项目数超过queueCapacity时,ThreadPoolTaskExecutor才会创建新线程。

3. 那么它们的区别是什么?

corePoolSizemaxPoolSize之间的区别似乎很明显。然而,它们的行为有一些微妙之处。

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

接下来,让我们看一些代码示例,了解每个属性何时起作用。

4. 示例

首先,假设我们有一个从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();
            }
        });
    }
}

让我们测试ThreadPoolTaskExecutor的默认配置,它定义了一个核心线程数为一个线程,无界的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());
    }
}

现在,让我们将corePoolSize修改为最多五个线程,并确保它按预期工作。因此,无论提交给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());
    }
}

同样地,我们可以将maxPoolSize增加到十个,同时将corePoolSize保持为五个。因此,我们只希望启动五个线程。需要注意的是,仅当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());
    }
}

此外,我们现在将queueCapacity增加到十个,并启动二十个线程。因此,我们现在期望总共启动十个线程:

@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());
    }
}

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

5. 结论

ThreadPoolTaskExecutor是围绕java.util.concurrent.ThreadPoolExecutor的强大抽象,提供了配置corePoolSizemaxPoolSizequeueCapacity的选项。在本教程中,我们看了corePoolSizemaxPoolSize属性,以及maxPoolSize如何与queueCapacity配合工作,让我们能够轻松地创建适用于任何用例的线程池。

你可以在Github 或者 Gitee上找到可用的代码。

0

评论区