线程池

频繁的创建、销毁线程和线程池,会给系统带来额外的开销。未经池化及统一管理的线程,则会导致系统内线程数上限不可控。这种情况下,随着访问数增加,系统内线程数持续增长,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;
}
相关推荐
葫芦和十三8 小时前
图解 MongoDB 07|索引类型:七种索引,七种访问形状
后端·mongodb·agent
朦胧之10 小时前
AI 编程-老项目改造篇
java·前端·后端
爱勇宝13 小时前
我做了一个只用来搜歌词的小 App
android·前端·后端
IT_陈寒14 小时前
SpringBoot自动配置坑了我一晚上,原来问题出在这
前端·人工智能·后端
SelectDB14 小时前
Litefuse 开源并推出单进程轻量模式,25 秒就能跑起来的 Agent 可观测与评估平台
运维·后端·自动化运维
SelectDB14 小时前
秒级弹性、最高降本 70%:SelectDB Serverless 如何重塑云数仓资源效率
大数据·后端·云原生
PinkSun14 小时前
Spring AI ChatMemory踩坑实录:重启丢数据、Agent丢记忆、对话溢出
后端·ai编程
壹方秘境14 小时前
我用Go语言开发了一个跨平台的HTTPS抓包和调试工具
前端·后端·ios
神秘面具男14 小时前
HarmonyOS 6.0跨端远程控制
前端·后端