Рубрики
Без рубрики

ThreadPoolTaskExecutor corePoolSize против maxPoolSize

Узнайте о свойствах corePoolSize и maxPoolSize в ThreadPoolTaskExecutor.

Автор оригинала: baeldung.

1. Обзор

Spring ThreadPoolTaskExecutor – это JavaBean, который обеспечивает абстракцию вокруг java.util.concurrent.ThreadPoolExecutor экземпляр и предоставляет его как Spring org.springframework.core.task.TaskExecutor . Кроме того, он легко настраивается с помощью свойств corePoolSize, maxPoolSize, queueCapacity, allowCoreThreadTimeOut и keepAliveSeconds. В этом уроке мы рассмотрим свойства corePoolSize и maxPoolSize .

2. corePoolSize против maxPoolSize

Пользователи, новички в этой абстракции, могут легко запутаться в различиях между двумя свойствами конфигурации. Поэтому давайте рассмотрим каждый отдельно.

2.1. Размер ядра

corePoolSize – это минимальное количество работников, которые должны оставаться в живых без тайм-аута. Это настраиваемое свойство ThreadPoolTaskExecutor . Однако ThreadPoolTaskExecutor абстракция делегирует установку этого значения базовому java.util.concurrent.ThreadPoolExecutor . Чтобы уточнить, все потоки могут тайм — аут-эффективно установить значение corePoolSize равным нулю, если мы установили allowCoreThreadTimeOut в true .

2.2. maxPoolSize

Напротив, maxPoolSize определяет максимальное количество потоков, которые когда-либо могут быть созданы . Аналогично, свойство maxPoolSize ThreadPoolTaskExecutor также делегирует свое значение базовому java.util.concurrent.ThreadPoolExecutor . Чтобы уточнить, maxPoolSize зависит от queueCapacity в том, что ThreadPoolTaskExecutor создаст новый поток только в том случае, если количество элементов в его очереди превысит queueCapacity .

3. Так в чем же разница?

Разница между corePoolSize и maxPoolSize может показаться очевидной. Однако есть некоторые тонкости, касающиеся их поведения.

Когда мы отправляем новую задачу в ThreadPoolTaskExecutor, он создает новый поток, если запущено меньше, чем corePoolSize потоков, даже если в пуле есть незанятые потоки, или если запущено меньше, чем maxPoolSize потоков и очередь, определенная queueCapacity , заполнена.

Далее давайте рассмотрим некоторый код, чтобы увидеть примеры того, когда каждое свойство вступает в действие.

4. Примеры

Во-первых, предположим, что у нас есть метод , который выполняет новые потоки из ThreadPoolTaskExecutor с именем start Threads :

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 , которая определяет 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());
    }
}

Теперь давайте изменим 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 на уровне пяти. В результате мы ожидаем запуска только пяти потоков. Чтобы уточнить, запускаются только пять потоков, потому что емкость очереди по-прежнему не ограничена:

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

Далее мы повторим предыдущий тест, но увеличим емкость очереди до десяти и запустим двадцать потоков. Таким образом, теперь мы ожидаем запустить в общей сложности десять потоков:

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

Аналогично, если бы мы установили емкость очереди на ноль и запустили только десять задач, у нас также было бы десять потоков в нашем ThreadPoolTaskExecutor .

5. Заключение

ThreadPoolTaskExecutor – это мощная абстракция вокруг java.util.concurrent.ThreadPoolExecutor , предоставляющий опции для настройки corePoolSize , maxPoolSize и queueCapacity . В этом уроке мы рассмотрели свойства corePoolSize и maxPoolSize , а также то, как maxPoolSize работает в тандеме с queueCapacity , что позволяет нам легко создавать пулы потоков для любого варианта использования.

Как всегда, вы можете найти код, доступный на Github .