Baeldung 翻译系列之Java并发基础:
Java中的concurrent包
Java中的Synchronized关键字
Future介绍
ThreadLocal介绍
Java线程的生命周期
如何杀掉一个Java线程
Java中的线程池介绍
实现Runnable接口还是继承Thread类
Java中的wait和notify方法
Runnable vs Callable
wait和sleep的区别
Thread.join方法介绍
Java中使用锁对象
ThreadPoolTaskExecutor中的corePoolSize和maxPoolSize
Java中的异步编程
1. 概述
Spring
的ThreadPoolTaskExecutor
是一个JavaBean
,它提供了对java.util.concurrent.ThreadPoolExecutor
实例的抽象,并将其作为Spring
的org.springframework.core.task.TaskExecutor
暴露出来。此外,通过corePoolSize
、maxPoolSize
、queueCapacity
、allowCoreThreadTimeOut
和keepAliveSeconds
等属性,它具有高度可配置性。在本教程中,我们将重点关注corePoolSize
和maxPoolSize
属性。
2. corePoolSize vs. maxPoolSize
对于这个抽象的新用户可能会对这两个配置属性的区别感到困惑。因此,让我们分别看一下它们。
2.1. corePoolSize
corePoolSize
是保持活动状态的最小工作线程数。它是ThreadPoolTaskExecutor
的可配置属性。然而,ThreadPoolTaskExecutor
的抽象将设置此值的工作委托给底层的java.util.concurrent.ThreadPoolExecutor
。需要注意的是,如果我们将allowCoreThreadTimeOut
设置为true
,所有线程都可能超时,从而将corePoolSize
的值设置为零。
2.2. maxPoolSize
相比之下,maxPoolSize
定义了可以创建的最大线程数。同样,ThreadPoolTaskExecutor
的maxPoolSize
属性也将其值委托给底层的java.util.concurrent.ThreadPoolExecutor
。需要注意的是,maxPoolSize
依赖于queueCapacity
,只有当队列中的项目数超过queueCapacity
时,ThreadPoolTaskExecutor
才会创建新线程。
3. 那么它们的区别是什么?
corePoolSize
和maxPoolSize
之间的区别似乎很明显。然而,它们的行为有一些微妙之处。
当我们向ThreadPoolTaskExecutor
提交一个新任务时,如果运行的线程数少于corePoolSize
,它将创建一个新线程,即使池中有空闲线程;或者如果运行的线程数少于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
的默认配置,它定义了一个核心线程数为一个线程,无界的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());
}
}
同样地,如果我们将queueCapacity
设置为零,并且只启动了十个任务,那么ThreadPoolTaskExecutor
中将有十个线程。
5. 结论
ThreadPoolTaskExecutor
是围绕java.util.concurrent.ThreadPoolExecutor
的强大抽象,提供了配置corePoolSize
、maxPoolSize
和queueCapacity
的选项。在本教程中,我们看了corePoolSize
和maxPoolSize
属性,以及maxPoolSize
如何与queueCapacity
配合工作,让我们能够轻松地创建适用于任何用例的线程池。
评论区