ThreadPoolTaskExecutor corePoolSize vs. maxPoolSize

1. Übersicht

Der Spring ThreadPoolTaskExecutor ist eine JavaBean, die eine Abstraktion um eine java.util.concurrent.ThreadPoolExecutor- Instanz bereitstellt und als Spring org.springframework.core.task.TaskExecutor verfügbar macht . Darüber hinaus ist es über die Eigenschaften von corePoolSize, maxPoolSize, queueCapacity, allowCoreThreadTimeOut und keepAliveSeconds in hohem Maße konfigurierbar. In diesem Tutorial werden die Eigenschaften corePoolSize und maxPoolSize beschrieben .

2. corePoolSize vs. maxPoolSize

Benutzer, die mit dieser Abstraktion noch nicht vertraut sind, können leicht über den Unterschied zwischen den beiden Konfigurationseigenschaften verwirrt werden. Schauen wir uns daher jedes einzeln an.

2.1. corePoolSize

Die corePoolSize ist die minimale Anzahl der Arbeitnehmer am Leben zu halten , ohne Zeitlimit. Es ist eine konfigurierbare Eigenschaft von ThreadPoolTaskExecutor . Die ThreadPoolTaskExecutor- Abstraktionsdelegierten setzen diesen Wert jedoch auf den zugrunde liegenden java.util.concurrent.ThreadPoolExecutor . Zur Verdeutlichung kann es bei allen Threads zu einer Zeitüberschreitung kommen. Setzen Sie den Wert von corePoolSize effektiv auf Null, wenn Sie allowCoreThreadTimeOut auf true gesetzt haben .

2.2. maxPoolSize

Im Gegensatz dazu definiert maxPoolSize die maximale Anzahl von Threads, die jemals erstellt werden können . In ähnlicher Weise delegiert die maxPoolSize- Eigenschaft von ThreadPoolTaskExecutor ihren Wert auch an den zugrunde liegenden java.util.concurrent.ThreadPoolExecutor . Zur Verdeutlichung hängt maxPoolSize von queueCapacity in diesem Thread ab. ThreadPoolTaskExecutor erstellt nur dann einen neuen Thread, wenn die Anzahl der Elemente in seiner Warteschlange queueCapacity überschreitet .

3. Was ist der Unterschied?

Der Unterschied zwischen corePoolSize und maxPoolSize scheint offensichtlich zu sein. Es gibt jedoch einige Feinheiten in Bezug auf ihr Verhalten.

Wenn wir eine neue Aufgabe an den ThreadPoolTaskExecutor senden, wird ein neuer Thread erstellt, wenn weniger als corePoolSize- Threads ausgeführt werden, selbst wenn im Pool inaktive Threads vorhanden sind oder wenn weniger als maxPoolSize- Threads ausgeführt werden und die von queueCapacity definierte Warteschlange voll ist.

Schauen wir uns als nächstes einen Code an, um Beispiele dafür zu sehen, wann jede Eigenschaft in Aktion tritt.

4. Beispiele

Nehmen wir zunächst an, wir haben eine Methode, die neue Threads aus dem ThreadPoolTaskExecutor mit dem Namen startThreads ausführt :

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

Testen wir die Standardkonfiguration von ThreadPoolTaskExecutor , die eine corePoolSize eines Threads, eine unbegrenzte maxPoolSize und eine unbegrenzte queueCapacity definiert . Daher erwarten wir, dass unabhängig von der Anzahl der gestarteten Aufgaben nur ein Thread ausgeführt wird:

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

Lassen Sie uns nun die corePoolSize auf maximal fünf Threads ändern und sicherstellen, dass sie sich wie angekündigt verhält. Daher erwarten wir, dass unabhängig von der Anzahl der an ThreadPoolTaskExecutor gesendeten Aufgaben fünf Threads gestartet werden :

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

Ebenso können wir die maxPoolSize auf zehn erhöhen , während die corePoolSize auf fünf belassen wird . Daher erwarten wir, dass nur fünf Threads gestartet werden. Zur Verdeutlichung werden nur fünf Threads gestartet, da die Warteschlangenkapazität noch unbegrenzt ist:

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

Außerdem wiederholen wir jetzt den vorherigen Test, erhöhen jedoch die Warteschlangenkapazität auf zehn und starten zwanzig Threads. Daher erwarten wir jetzt insgesamt zehn Threads:

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

Wenn wir die queueCapactity auf Null gesetzt und nur zehn Aufgaben gestartet hätten, hätten wir auch zehn Threads in unserem ThreadPoolTaskExecutor .

5. Schlussfolgerung

ThreadPoolTaskExecutor ist eine leistungsstarke Abstraktion um einen java.util.concurrent.ThreadPoolExecutor , die Optionen zum Konfigurieren von corePoolSize , maxPoolSize und queueCapacity bereitstellt . In diesem Tutorial haben wir uns die Eigenschaften corePoolSize und maxPoolSize sowie die Funktionsweise von maxPoolSize in Verbindung mit queueCapacity angesehen , sodass wir problemlos Thread-Pools für jeden Anwendungsfall erstellen können.

Wie immer finden Sie den Code auf Github.