☠博主专栏 : <mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关>
♝博主的话 : 搬的每块砖,皆为峰峦之基;公众号搜索「码到三十五」关注这个爱发技术干货的coder,一起筑基
Java中线程池是管理多线程任务的工具。标准的ThreadPoolExecutor
允许我们设置核心线程数、最大线程数、队列容量等参数,但这些参数在初始化后无法动态调整。有时候,可能需要根据系统负载动态调整线程池参数,以优化性能。
1. 动态线程池的需求
在某些场景下,线程池的负载可能会随时间变化。例如:
- 流量波动:系统在高峰期需要更多线程处理请求,而在低峰期则不需要。
- 资源限制:系统资源(如CPU、内存)可能随时间变化,需要动态调整线程池参数以适配当前资源。
标准的ThreadPoolExecutor
无法在运行时动态调整核心线程数、最大线程数等参数,因此需要实现一个动态线程池。
2. 动态线程池的原理
Java动态改变线程池核心参数的原理主要依赖于ThreadPoolExecutor
类本身提供的灵活性。ThreadPoolExecutor
是Java标准库中用于管理线程池的核心类,它允许在运行时动态调整一些关键参数,如核心线程数、最大线程数、线程空闲时间等。
2.1. ThreadPoolExecutor
的核心参数
- corePoolSize:核心线程数,线程池中始终保持存活的线程数量。
- maximumPoolSize:最大线程数,线程池中允许的最大线程数量。
- keepAliveTime:线程空闲时间,当线程池中的线程数量超过核心线程数时,多余的线程在空闲时间超过
keepAliveTime
后会被回收。 - workQueue:任务队列,用于存放待执行的任务。
- threadFactory:线程工厂,用于创建新线程。
- rejectedExecutionHandler:拒绝策略,当任务队列已满且线程数达到最大线程数时,如何处理新提交的任务。
这些参数在ThreadPoolExecutor
初始化时设置,但部分参数可以在运行时动态修改。
2.2. 动态调整核心参数的原理
ThreadPoolExecutor
提供了以下方法,允许在运行时动态调整核心参数:
(1)setCorePoolSize(int corePoolSize)
- 作用:动态设置核心线程数。
- 原理:
- 如果新的
corePoolSize
大于当前的核心线程数,线程池会创建新的线程,直到线程数达到新的corePoolSize
。 - 如果新的
corePoolSize
小于当前的核心线程数,多余的线程会在空闲时被回收(根据keepAliveTime
)。 - 如果任务队列中有等待的任务,且当前线程数小于新的
corePoolSize
,线程池会立即创建新线程来处理任务。
- 如果新的
(2)setMaximumPoolSize(int maximumPoolSize)
- 作用:动态设置最大线程数。
- 原理:
- 如果新的
maximumPoolSize
小于当前的最大线程数,多余的线程会在空闲时被回收。 - 如果新的
maximumPoolSize
大于当前的最大线程数,且任务队列已满,线程池会创建新线程来处理任务。
- 如果新的
(3)setKeepAliveTime(long time, TimeUnit unit)
- 作用:动态设置线程的空闲时间。
- 原理:
- 当线程池中的线程数量超过核心线程数时,多余的线程在空闲时间超过
keepAliveTime
后会被回收。 - 该方法会立即生效,影响当前和未来的空闲线程。
- 当线程池中的线程数量超过核心线程数时,多余的线程在空闲时间超过
(4)allowCoreThreadTimeOut(boolean value)
- 作用:允许核心线程在空闲时被回收。
- 原理:
- 如果设置为
true
,核心线程在空闲时间超过keepAliveTime
后也会被回收。 - 如果设置为
false
,核心线程会一直存活,即使处于空闲状态。
- 如果设置为
2.3. 动态调整参数的内部实现
ThreadPoolExecutor
的内部实现基于一个AtomicInteger
类型的变量ctl
,它同时存储了线程池的状态(如运行中、关闭等)和当前线程数。动态调整参数时,ThreadPoolExecutor
会通过以下步骤实现:
-
参数验证:
- 在调用
setCorePoolSize
或setMaximumPoolSize
时,会验证新参数是否合法(如corePoolSize
不能大于maximumPoolSize
)。
- 在调用
-
线程创建或回收:
- 如果新的
corePoolSize
大于当前线程数,线程池会调用addWorker
方法创建新线程。 - 如果新的
corePoolSize
小于当前线程数,多余的线程会在空闲时被回收。
- 如果新的
-
任务处理:
- 如果任务队列中有等待的任务,且当前线程数小于新的
corePoolSize
,线程池会立即创建新线程来处理任务。
- 如果任务队列中有等待的任务,且当前线程数小于新的
-
状态更新:
- 更新内部状态(如
corePoolSize
、maximumPoolSize
等),并通知相关组件(如任务队列、线程工厂等)。
- 更新内部状态(如
3. 线程池动态调整参数的用法
动态调整线程池核心参数:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.LinkedBlockingQueue;
public class DynamicThreadPoolExecutor extends ThreadPoolExecutor {
public DynamicThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
// 动态设置核心线程数
public void setCorePoolSize(int corePoolSize) {
if (corePoolSize < 0 || corePoolSize > getMaximumPoolSize()) {
throw new IllegalArgumentException(".........");
}
super.setCorePoolSize(corePoolSize);
}
// 动态设置最大线程数
public void setMaximumPoolSize(int maximumPoolSize) {
if (maximumPoolSize < 1 || maximumPoolSize < getCorePoolSize()) {
throw new IllegalArgumentException(".....");
}
super.setMaximumPoolSize(maximumPoolSize);
}
// 动态设置线程空闲时间
public void setKeepAliveTime(long time, TimeUnit unit) {
super.setKeepAliveTime(time, unit);
}
public static void main(String[] args) {
DynamicThreadPoolExecutor executor = new DynamicThreadPoolExecutor(
2, // 初始核心线程数
4, // 初始最大线程数
60, // 线程空闲时间
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10)
);
// 提交任务
for (int i = 0; i < 10; i++) {
executor.execute(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Task executed by " + Thread.currentThread().getName());
});
}
// 动态调整线程池参数
executor.setCorePoolSize(4);
executor.setMaximumPoolSize(8);
executor.setKeepAliveTime(30, TimeUnit.SECONDS);
// 关闭线程池
executor.shutdown();
}
}
4. 集成nacos实现线程池动态调整参数
4.1 Nacos中创建配置
Nacos控制台中创建配置文件,内容:
threadpool:
corePoolSize: 2
maxPoolSize: 4
queueCapacity: 10
keepAliveSeconds: 60
4.2 线程池配置属性类
创建配置类,用于从Nacos中读取线程池配置,并动态调整线程池参数。
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "threadpool")
@RefreshScope // 支持动态刷新
public class ThreadPoolProperties {
private int corePoolSize;
private int maxPoolSize;
private int queueCapacity;
private int keepAliveSeconds;
public int getCorePoolSize() {
return corePoolSize;
}
public void setCorePoolSize(int corePoolSize) {
this.corePoolSize = corePoolSize;
}
public int getMaxPoolSize() {
return maxPoolSize;
}
public void setMaxPoolSize(int maxPoolSize) {
this.maxPoolSize = maxPoolSize;
}
public int getQueueCapacity() {
return queueCapacity;
}
public void setQueueCapacity(int queueCapacity) {
this.queueCapacity = queueCapacity;
}
public int getKeepAliveSeconds() {
return keepAliveSeconds;
}
public void setKeepAliveSeconds(int keepAliveSeconds) {
this.keepAliveSeconds = keepAliveSeconds;
}
}
4.3 线程池配置类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
public class ThreadPoolConfig {
@Autowired
private ThreadPoolProperties threadPoolProperties;
@Bean
@RefreshScope // 支持动态刷新
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(threadPoolProperties.getCorePoolSize());
executor.setMaxPoolSize(threadPoolProperties.getMaxPoolSize());
executor.setQueueCapacity(threadPoolProperties.getQueueCapacity());
executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds());
executor.setThreadNamePrefix("DynamicThreadPool-");
executor.initialize();
return executor;
}
}
4.4.动态调整线程池参数
通过监听Nacos配置变化,动态调整线程池参数。监听配置刷新事件:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
@Component
@RefreshScope
public class ThreadPoolRefresher {
@Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Autowired
private ThreadPoolProperties threadPoolProperties;
@EventListener
public void onRefreshScopeRefreshed(RefreshScopeRefreshedEvent event) {
// 动态调整线程池参数
threadPoolTaskExecutor.setCorePoolSize(threadPoolProperties.getCorePoolSize());
threadPoolTaskExecutor.setMaxPoolSize(threadPoolProperties.getMaxPoolSize());
threadPoolTaskExecutor.setQueueCapacity(threadPoolProperties.getQueueCapacity());
threadPoolTaskExecutor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds());
System.out.println("Thread pool parameters refreshed!");
System.out.println("Core Pool Size: " + threadPoolTaskExecutor.getCorePoolSize());
System.out.println("Max Pool Size: " + threadPoolTaskExecutor.getMaxPoolSize());
System.out.println("Queue Capacity: " + threadPoolTaskExecutor.getQueueCapacity());
System.out.println("Keep Alive Seconds: " + threadPoolTaskExecutor.getKeepAliveSeconds());
}
}
Spring Cloud中,RefreshScopeRefreshedEvent 是一个事件类,用于表示 @RefreshScope 注解的Bean被刷新的事件。当配置中心(如Nacos)中的配置发生变化时,Spring Cloud会触发这个事件,通知所有标记为 @RefreshScope 的Bean重新加载配置。
RefreshScopeRefreshedEvent 的作用:
动态刷新配置:当Nacos中的配置发生变化时,Spring Cloud会发布 RefreshScopeRefreshedEvent 事件。
重新初始化Bean:所有标记为 @RefreshScope 的Bean会重新初始化,加载最新的配置值。
自定义逻辑:可以通过监听 RefreshScopeRefreshedEvent 事件,在配置刷新时执行自定义逻辑(如动态调整线程池参数)
4.5. 测试动态调整
在Nacos控制台中修改threadpool.corePoolSize
、threadpool.maxPoolSize
等参数:
threadpool:
corePoolSize: 4
maxPoolSize: 8
queueCapacity: 20
keepAliveSeconds: 30
当Nacos配置发生变化时,Spring Boot会自动刷新 @RefreshScope
的Bean,并触发 RefreshScopeRefreshedEvent
事件。日志中会输出:
Thread pool parameters refreshed!
Core Pool Size: 4
Max Pool Size: 8
Queue Capacity: 20
Keep Alive Seconds: 30
4.5. nacos配置动态参数流程总结
通过结合Spring Boot和Nacos配置中心,可以实现线程池参数的动态调整。关键点包括:
- 使用
@RefreshScope
注解支持配置的动态刷新。 - 通过
ThreadPoolTaskExecutor
动态调整线程池参数。 - 监听
RefreshScopeRefreshedEvent
事件,实时更新线程池配置。