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
В corePoolSize е минималният брой на работниците, за да се запази жив без времето навън. Това е конфигурируемо свойство на ThreadPoolTaskExecutor . Въпреки това абстракцията на ThreadPoolTaskExecutor делегира задаването на тази стойност на основния java.util.concurrent.ThreadPoolExecutor . За да се изясни, всички теми, може Time Out - ефективно определяне на стойността на corePoolSize до нула, ако сте задали allowCoreThreadTimeOut да е вярно .
2.2. maxPoolSize
За разлика от това, maxPoolSize определя максималния брой нишки, които някога могат да бъдат създадени . По същия начин свойството maxPoolSize на ThreadPoolTaskExecutor също делегира стойността си на основния java.util.concurrent.ThreadPoolExecutor . За да се изясни, maxPoolSize зависи от queueCapacity, тъй като ThreadPoolTaskExecutor ще създаде нова нишка само ако броят на елементите в нейната опашка надвишава queueCapacity .
3. И така, каква е разликата?
Разликата между corePoolSize и maxPoolSize може да изглежда очевидна. Има обаче някои тънкости по отношение на тяхното поведение.
Когато изпратим нова задача на ThreadPoolTaskExecutor, тя създава нова нишка, ако се изпълняват по-малко от corePoolSize нишки, дори ако в пула има неактивни нишки или ако се изпълняват по-малко от maxPoolSize нишки и опашката, определена от queueCapacity, е пълна.
След това нека разгледаме някои кодове, за да видим примери за това, кога всяко свойство започва да действа.
4. Примери
Първо, да кажем, че имаме метод, който изпълнява нови нишки, от ThreadPoolTaskExecutor , наречен 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(); } }); } }
Нека тестваме конфигурацията по подразбиране на 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 на пет. В резултат на това очакваме да започнем само пет нишки. За да се изясни, стартират само пет нишки, тъй като queueCapacity все още е неограничен:
@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 до десет и ще започнем двадесет нишки. Затова сега очакваме да започнем общо десет нишки:
@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 на нула и стартирахме само десет задачи, щяхме да имаме и десет нишки в нашия ThreadPoolTaskExecutor .
5. Заключение
ThreadPoolTaskExecutor е мощна абстракция около java.util.concurrent.ThreadPoolExecutor , предоставяща опции за конфигуриране на corePoolSize , maxPoolSize и queueCapacity . В този урок разгледахме свойствата corePoolSize и maxPoolSize , както и как maxPoolSize работи в тандем с queueCapacity , което ни позволява лесно да създаваме пулове от нишки за всеки случай на употреба.
Както винаги, можете да намерите кода, наличен в Github.