线程池

频繁的创建、销毁线程和线程池,会给系统带来额外的开销。未经池化及统一管理的线程,则会导致系统内线程数上限不可控。这种情况下,随着访问数增加,系统内线程数持续增长,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;
}
相关推荐
JohnYan1 小时前
Bun技术评估 - 03 HTTP Server
javascript·后端·bun
周末程序猿1 小时前
Linux高性能网络编程十谈|C++11实现22种高并发模型
后端·面试
ZHOU_WUYI1 小时前
Flask与Celery 项目应用(shared_task使用)
后端·python·flask
冒泡的肥皂2 小时前
强大的ANTLR4语法解析器入门demo
后端·搜索引擎·编程语言
IT_陈寒2 小时前
Element Plus 2.10.0 重磅发布!新增Splitter组件
前端·人工智能·后端
有梦想的攻城狮3 小时前
spring中的@RabbitListener注解详解
java·后端·spring·rabbitlistener
Java水解3 小时前
MySQL DQL全面解析:从入门到精通
后端·mysql
Aurora_NeAr3 小时前
Apache Spark详解
大数据·后端·spark
程序员岳焱3 小时前
Java 程序员成长记(二):菜鸟入职之 MyBatis XML「陷阱」
java·后端·程序员
hello早上好3 小时前
BeanFactory 实现
后端·spring·架构