1. 概要

Spring ThreadPoolTaskExecutor は、 java.util.concurrent.ThreadPoolExecutor インスタンスを抽象化し、Spring org.springframework.core.task.TaskExecutorとして公開するJavaBeanです。 。 さらに、 corePoolSize、maxPoolSize、queueCapacity、allowCoreThreadTimeOut keepAliveSecondsのプロパティを使用して高度に構成できます。このチュートリアルでは、corePoolSizemaxPoolSizeプロパティ。

2. corePoolSize対。 maxPoolSize

この抽象化に不慣れなユーザーは、2つの構成プロパティの違いについて簡単に混乱する可能性があります。 したがって、それぞれを個別に見てみましょう。

2.1. corePoolSize

corePoolSizeは、タイムアウトせずに存続するワーカーの最小数です。 これは、ThreadPoolTaskExecutorの構成可能なプロパティです。 ただし、 ThreadPoolTaskExecutor 抽象化は、この値の設定を基になる java .util.concurrent.ThreadPoolExecutor に委任します。明確にするために、すべてのスレッドが効果的にタイムアウトする場合があります。 allowCoreThreadTimeOuttrueに設定した場合は、corePoolSizeの値をゼロに設定します。

2.2. maxPoolSize

対照的に、 maxPoolSizeは、これまでに作成できるスレッドの最大数を定義します。 同様に、ThreadPoolTaskExecutormaxPoolSizeプロパティも、その値を基になるjava.util.concurrent.ThreadPoolExecutorに委任します。 明確にするために、maxPoolSizeはqueueCapacityに依存し、ThreadPoolTaskExecutorはキュー内のアイテム数がqueueCapacityを超えた場合にのみ新しいスレッドを作成します。

3. では、違いは何ですか?

corePoolSizemaxPoolSizeの違いは明らかなように思われるかもしれません。 ただし、それらの動作に関しては微妙な点がいくつかあります。

ThreadPoolTaskExecutorに新しいタスクを送信すると、は、 corePoolSize 未満のスレッドが実行されている場合、プールにアイドル状態のスレッドがある場合でも、未満の場合でも新しいスレッドを作成します。 X208X] 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 のデフォルト構成をテストしてみましょう。これは、1つのスレッドの corePoolSize 、無制限の maxPoolSize、、および無制限のqueueCapacityを定義します。 その結果、開始するタスクの数に関係なく、実行されるスレッドは1つだけになると予想されます。

@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 を最大5スレッドに変更して、アドバタイズされたとおりに動作することを確認しましょう。 その結果、 ThreadPoolTaskExecutor に送信されたタスクの数に関係なく、5つのスレッドが開始されると予想されます。

@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 を10に増やし、corePoolSizeを5のままにすることができます。 結果として、5つのスレッドのみを開始することを期待しています。 明確にするために、 queueCapacity はまだ制限されていないため、5つのスレッドのみが開始します。

@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 を10にインクリメントし、20スレッドを開始します。 したがって、合計で10個のスレッドを開始する予定です。

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

同様に、 queueCapactity をゼロに設定し、10個のタスクのみを開始した場合、ThreadPoolTaskExecutorにも10個のスレッドがあります。

5. 結論

ThreadPoolTaskExecutor は、 java.util.concurrent.ThreadPoolExecutor を強力に抽象化したもので、 corePoolSize maxPoolSize 、および[X175X]を構成するためのオプションを提供します。 X204X]queueCapacity。 このチュートリアルでは、corePoolSizeおよびmaxPoolSizeプロパティと、maxPoolSizequeueCapacityと連携してどのように機能するかを確認しました。あらゆるユースケースのスレッドプールを簡単に作成できます。

いつものように、Github利用可能なコードを見つけることができます。