线程池

频繁的创建、销毁线程和线程池,会给系统带来额外的开销。未经池化及统一管理的线程,则会导致系统内线程数上限不可控。这种情况下,随着访问数增加,系统内线程数持续增长,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;
}
相关推荐
用户40966601317515 小时前
MyBatis、MyBatis-Plus、通用 Mapper:一张图说清三者的血缘关系
后端
爬楼的猪6 小时前
WSL 的 GUI 为什么总是卡卡的
后端
程序猿DD7 小时前
OPC必备!在 Cloudflare Worker 上免费部署 AI 网关:集中管理与供应 Token
后端
LucianaiB7 小时前
遇到 Seed Evolving,我终于把脑海里的小说世界图谱做出来了:《斗破苍穹》篇
后端
JuiceFS8 小时前
GPFS、Alluxio、JuiceFS 怎么选?一文看懂架构与适用场景
人工智能·后端
武子康8 小时前
1.2GB 离线语音 Agent 真正值得复用的不是 908ms:四阶段职责 + 状态感知 Tool Schema + 可观测时间锚点
人工智能·后端·agent
掘金者阿豪8 小时前
那些年踩过的 MySQL 迁移坑,这次终于不用绕了
后端
用户7713970207068 小时前
深夜食堂:那个让我加班到凌晨的 ! 符号
后端
swipe8 小时前
03|Axios 请求进了后端之后:Controller、Request、Response 是怎么接住它的?
前端·后端·全栈
swipe9 小时前
02|从 `pnpm dev` 到 Spring Boot 启动:后端服务到底怎么跑起来?
前端·后端·全栈