ThreadPoolTaskExecutor corePoolSize rispetto a maxPoolSize

1. Panoramica

Spring ThreadPoolTaskExecutor è un JavaBean che fornisce un'astrazione attorno a un'istanza java.util.concurrent.ThreadPoolExecutor e la espone come Spring org.springframework.core.task.TaskExecutor . Inoltre, è altamente configurabile tramite le proprietà di corePoolSize, maxPoolSize, queueCapacity, allowCoreThreadTimeOut e keepAliveSeconds. In questo tutorial, esamineremo le proprietà corePoolSize e maxPoolSize .

2. corePoolSize vs. maxPoolSize

Gli utenti nuovi a questa astrazione possono facilmente confondersi sulla differenza tra le due proprietà di configurazione. Pertanto, esaminiamo ciascuno in modo indipendente.

2.1. corePoolSize

Il corePoolSize è il numero minimo di lavoratori per mantenere vivo senza timeout. È una proprietà configurabile di ThreadPoolTaskExecutor . Tuttavia, l' astrazione ThreadPoolTaskExecutor delega l'impostazione di questo valore sul java.util.concurrent.ThreadPoolExecutor sottostante . Per chiarire, tutti i thread potrebbero scadere, impostando effettivamente il valore di corePoolSize su zero se abbiamo impostato allowCoreThreadTimeOut su true .

2.2. maxPoolSize

Al contrario, maxPoolSize definisce il numero massimo di thread che possono essere creati . Allo stesso modo, la proprietà maxPoolSize di ThreadPoolTaskExecutor delega anche il proprio valore al java.util.concurrent.ThreadPoolExecutor sottostante . Per chiarire, maxPoolSize dipende da queueCapacity in quel ThreadPoolTaskExecutor creerà un nuovo thread solo se il numero di elementi nella sua coda supera queueCapacity .

3. Allora qual è la differenza?

La differenza tra corePoolSize e maxPoolSize può sembrare evidente. Tuttavia, ci sono alcune sottigliezze riguardo al loro comportamento.

Quando inviamo una nuova attività a ThreadPoolTaskExecutor, crea un nuovo thread se sono in esecuzione meno di corePoolSize thread, anche se ci sono thread inattivi nel pool, o se sono in esecuzione meno di maxPoolSize thread e la coda definita da queueCapacity è piena.

Successivamente, diamo un'occhiata al codice per vedere esempi di quando ogni proprietà entra in azione.

4. Esempi

Innanzitutto, supponiamo di avere un metodo che esegue nuovi thread, da ThreadPoolTaskExecutor , denominato startThreads :

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

Testiamo la configurazione predefinita di ThreadPoolTaskExecutor , che definisce un corePoolSize di un thread, un maxPoolSize illimitato e un queueCapacity illimitato . Di conseguenza, ci aspettiamo che, indipendentemente dal numero di attività che iniziamo, avremo solo un thread in esecuzione:

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

Ora, modifichiamo corePoolSize a un massimo di cinque thread e assicuriamoci che si comporti come pubblicizzato. Di conseguenza, ci aspettiamo che vengano avviati cinque thread indipendentemente dal numero di attività inviate a 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()); } }

Allo stesso modo, possiamo incrementare maxPoolSize a dieci lasciando corePoolSize a cinque. Di conseguenza, prevediamo di avviare solo cinque thread. Per chiarire, vengono avviati solo cinque thread perché queueCapacity è ancora illimitato:

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

Inoltre, ripeteremo ora il test precedente ma incrementeremo la capacità di coda a dieci e avvieremo venti thread. Pertanto, ora ci aspettiamo di avviare dieci thread in totale:

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

Allo stesso modo, se avessimo impostato queueCapactity su zero e avessimo avviato solo dieci attività, avremmo anche dieci thread nel nostro ThreadPoolTaskExecutor .

5. conclusione

ThreadPoolTaskExecutor è una potente astrazione attorno a java.util.concurrent.ThreadPoolExecutor , che fornisce opzioni per la configurazione di corePoolSize , maxPoolSize e queueCapacity . In questo tutorial, abbiamo esaminato le proprietà corePoolSize e maxPoolSize , nonché il modo in cui maxPoolSize funziona in tandem con queueCapacity , permettendoci di creare facilmente pool di thread per qualsiasi caso d'uso.

Come sempre, puoi trovare il codice disponibile su Github.