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.