线程池

频繁的创建、销毁线程和线程池,会给系统带来额外的开销。未经池化及统一管理的线程,则会导致系统内线程数上限不可控。这种情况下,随着访问数增加,系统内线程数持续增长,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;
}
相关推荐
SomeB1oody10 分钟前
【RustyML入门】7.4. 按需裁剪与模块化集成
开发语言·后端·机器学习·rust·教程
罗超驿13 分钟前
SpringBoot 快速入门
java·spring boot·后端
卷无止境1 小时前
除了开发api,FastAPI其实也可以配合jinja2模板写页面
后端·python·fastapi
newerp1 小时前
Redis 操作与缓存策略
后端·程序员·go
newerp1 小时前
GORM ORM 基础
后端·程序员·go
小岛前端1 小时前
AI Skills 已经封神,但新的问题却越来越严重!
前端·后端·github
拖孩1 小时前
一个人 + AI 做的小程序,上线 15 天赚了 10 块 5
前端·后端·微信小程序
newerp1 小时前
CRUD 操作与预处理语句
后端·程序员·go
wei_shuo1 小时前
KES 数据同步与ETL实战:数据集成、转换与实时同步方案
后端
乒乓狂魔14786739970001 小时前
LangGraph:用一张图,编排一群 AI 助手
后端