频繁的创建、销毁线程和线程池,会给系统带来额外的开销。未经池化及统一管理的线程,则会导致系统内线程数上限不可控。这种情况下,随着访问数增加,系统内线程数持续增长,CPU负载逐步提高。极端情况下,甚至可能会导致CPU资源被吃满,整个服务不可用。
为了解决上述问题,可增加统一线程池配置,替换掉自建线程和线程池。下面介绍几种创建线程池的方式。
Executors工具类
java
// 创建一个固定数量线程的线程池 还有其他线程池,小伙伴们可以自己点进源码看看哦
ExecutorService executorService = Executors.newFixedThreadPool(1);
优点:简单,方便
缺点 :Excutors
创建的线程池用的无界队列,有oom的风险
自定义线程池
ExecutorService 属于并发包concurrent下
java
// 小伙伴们要记住这7个参数具体的含义哦
ExecutorService executor = new ThreadPoolExecutor(
3, 5, 200,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(5), // 阻塞队列
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
7大核心参数,简单理解
corePoolSize
: 核心线程数量(常驻),永不淘汰
maximumPoolSize
: 最大线程池数量,>= corePoolSize数量
keepAliveTime
: 当maximumPoolSize > corePoolSize时,线程空闲时的存活时间
TimeUnit unit
: keepAliveTime存活时间的单位
workQueue
: 阻塞队列的类型
threadFactory
: 线程池工厂
handler
: 拒绝策略
如何优雅停机
java
// 创建一个固定大小的线程池,核心线程数=最大线程数=2
ThreadPoolExecutor executorService = new ThreadPoolExecutor(
2, 2, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(2), // 队列大小为2
new ThreadPoolExecutor.CallerRunsPolicy() // 设置拒绝策略为CallerRunsPolicy
);
// 提交5个任务到线程池
for (int i = 0; i < 5; i++) {
final int taskId = i;
executorService.execute(() -> {
System.out.println("Executing task " + taskId + " by " + Thread.currentThread().getName());
try {
// 模拟耗时任务
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 等待所有任务完成 优雅停机
executorService.shutdown(); // 不接受新任务,但会等待已提交任务完成
try {
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) { // 等待任务的完成,如果超出时间还未完成,则会返回false,如果提前完成返回true
executorService.shutdownNow(); // 取消所有任务
}
} catch (InterruptedException e) {
executorService.shutdownNow(); // 在中断的情况下也取消所有任务
}
由于本次使用了CallerRunsPolicy拒绝策略,所以第五个任务执行是由main线程来执行的。它的行为是:如果线程池和队列都满了,那么就直接在提交任务的线程中运行这个任务
spring中线程池
ThreadPoolTaskExecutor所属spring管理
java
@Configuration
@EnableAsync
public class ThreadPoolConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
return myExecutor();
}
@Bean("my-myExecutor")
@Primary
public ThreadPoolTaskExecutor myExecutor() {
// ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,2,4L,);
// spring所管理的,赋予了优雅停机 ExecutorConfigurationSupport -> DisposableBean
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 优雅停机 当spring关闭时,调用destroy时会调用shutdown方法
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setCorePoolSize(10);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(200);
executor.setThreadNamePrefix("myproject-executor-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//满了调用线程执行,认为重要任务
// 添加异常处理,因为如果添加,出现异常时会被吃掉,不打印
executor.setThreadFactory(new MyThreadFactory(executor));
executor.initialize();
return executor;
}
// 当想要使用时:
@Autowired
@Qualifier("my-myExecutor") // 指定bean的名称
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
}