千家信息网

Spring中怎么使用ThreadPoolTaskExecutor自定义线程池及异步调用

发表于:2025-02-02 作者:千家信息网编辑
千家信息网最后更新 2025年02月02日,这篇文章主要介绍了Spring中怎么使用ThreadPoolTaskExecutor自定义线程池及异步调用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Spring中怎么
千家信息网最后更新 2025年02月02日Spring中怎么使用ThreadPoolTaskExecutor自定义线程池及异步调用

这篇文章主要介绍了Spring中怎么使用ThreadPoolTaskExecutor自定义线程池及异步调用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Spring中怎么使用ThreadPoolTaskExecutor自定义线程池及异步调用文章都会有所收获,下面我们一起来看看吧。

    多线程一直是工作或面试过程中的高频知识点,今天给大家分享一下使用 ThreadPoolTaskExecutor 来自定义线程池和实现异步调用多线程。

    一、ThreadPoolTaskExecutor

    本文采用 Executors 的工厂方法进行配置。

    1、将线程池用到的参数定义到配置文件中

    在项目的 resources 目录下创建 executor.properties 文件,并添加如下配置:

    # 异步线程配置# 核心线程数async.executor.thread.core_pool_size=5# 最大线程数async.executor.thread.max_pool_size=8# 任务队列大小async.executor.thread.queue_capacity=2# 线程池中线程的名称前缀async.executor.thread.name.prefix=async-service-# 缓冲队列中线程的空闲时间async.executor.thread.keep_alive_seconds=100

    2、Executors 的工厂配置

    2.1、配置详情
    @Configuration// @PropertySource是找的target目录下classes目录下的文件,resources目录下的文件编译后会生成在classes目录@PropertySource(value = {"classpath:executor.properties"}, ignoreResourceNotFound=false, encoding="UTF-8")@Slf4jpublic class ExecutorConfig {    @Value("${async.executor.thread.core_pool_size}")    private int corePoolSize;    @Value("${async.executor.thread.max_pool_size}")    private int maxPoolSize;    @Value("${async.executor.thread.queue_capacity}")    private int queueCapacity;    @Value("${async.executor.thread.name.prefix}")    private String namePrefix;    @Value("${async.executor.thread.keep_alive_seconds}")    private int keepAliveSeconds;    @Bean(name = "asyncTaskExecutor")    public ThreadPoolTaskExecutor taskExecutor() {        log.info("启动");        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();        // 核心线程数        executor.setCorePoolSize(corePoolSize);        // 最大线程数        executor.setMaxPoolSize(maxPoolSize);        // 任务队列大小        executor.setQueueCapacity(queueCapacity);        // 线程前缀名        executor.setThreadNamePrefix(namePrefix);        // 线程的空闲时间        executor.setKeepAliveSeconds(keepAliveSeconds);        // 拒绝策略        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());        // 线程初始化        executor.initialize();        return executor;    }}
    2.2、注解说明
    • @Configuration:Spring 容器在启动时,会加载带有 @Configuration 注解的类,对其中带有 @Bean 注解的方法进行处理。

    • @Bean:是一个方法级别上的注解,主要用在 @Configuration 注解的类里,也可以用在 @Component 注解的类里。添加的 bean 的 id 为方法名。

    • @PropertySource:加载指定的配置文件。value 值为要加载的配置文件,ignoreResourceNotFound 意思是如果加载的文件找不到,程序是否忽略它。默认为 false 。如果为 true ,则代表加载的配置文件不存在,程序不报错。在实际项目开发中,最好设置为 false 。如果 application.properties 文件中的属性与自定义配置文件中的属性重复,则自定义配置文件中的属性值被覆盖,加载的是 application.properties 文件中的配置属性。

    • @Slf4j:lombok 的日志输出工具,加上此注解后,可直接调用 log 输出各个级别的日志。

    • @Value:调用配置文件中的属性并给属性赋予值。

    2.3、线程池配置说明
    • 核心线程数:线程池创建时候初始化的线程数。当线程数超过核心线程数,则超过的线程则进入任务队列。

    • 最大线程数:只有在任务队列满了之后才会申请超过核心线程数的线程。不能小于核心线程数。

    • 任务队列:线程数大于核心线程数的部分进入任务队列。如果任务队列足够大,超出核心线程数的线程不会被创建,它会等待核心线程执行完它们自己的任务后再执行任务队列的任务,而不会再额外地创建线程。举例:如果有20个任务要执行,核心线程数:10,最大线程数:20,任务队列大小:2。则系统会创建18个线程。这18个线程有执行完任务的,再执行任务队列中的任务。

    • 线程的空闲时间:当 线程池中的线程数量 大于 核心线程数 时,如果某线程空闲时间超过 keepAliveTime ,线程将被终止。这样,线程池可以动态的调整池中的线程数。

    • 拒绝策略:如果(总任务数 - 核心线程数 - 任务队列数)-(最大线程数 - 核心线程数)> 0 的话,则会出现线程拒绝。举例:( 12 - 5 - 2 ) - ( 8 - 5 ) > 0,会出现线程拒绝。线程拒绝又分为 4 种策略,分别为:

    • CallerRunsPolicy():交由调用方线程运行,比如 main 线程。

    • AbortPolicy():直接抛出异常。

    • DiscardPolicy():直接丢弃。

    • DiscardOldestPolicy():丢弃队列中最老的任务。

    2.4、线程池配置个人理解
    • 当一个任务被提交到线程池时,首先查看线程池的核心线程是否都在执行任务。如果没有,则选择一条线程执行任务。

    • 如果都在执行任务,查看任务队列是否已满。如果不满,则将任务存储在任务队列中。核心线程执行完自己的任务后,会再处理任务队列中的任务。

    • 如果任务队列已满,查看线程池(最大线程数控制)是否已满。如果不满,则创建一条线程去执行任务。如果满了,就按照策略处理无法执行的任务。

    二、异步调用线程

    通常 ThreadPoolTaskExecutor 是和 @Async 一起使用。在一个方法上添加 @Async 注解,表明是异步调用方法函数。

    @Async 后面加上线程池的方法名或 bean 名称,表明异步线程会加载线程池的配置。

    @Component@Slf4jpublic class ThreadTest {    /**     * 每10秒循环一次,一个线程共循环10次。     */    @Async("asyncTaskExecutor")    public void ceshi3() {        for (int i = 0; i <= 10; i  ) {            log.info("ceshi3: "   i);            try {                Thread.sleep(2000 * 5);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

    备注:一定要在启动类上添加 @EnableAsync 注解,这样 @Async 注解才会生效。

    三、多线程使用场景

    1、定时任务 @Scheduled

    // 在启动类上添加 @EnableScheduling 注解@SpringBootApplication@EnableSchedulingpublic class SpringBootStudyApplication {   public static void main(String[] args) {      SpringApplication.run(SpringBootStudyApplication.class, args);   }}
    // @Component 注解将定时任务类纳入 spring bean 管理。@Componentpublic class listennerTest3 {    @Autowired    private ThreadTest t;        // 每1分钟执行一次ceshi3()方法    @Scheduled(cron = "0 0/1 * * * ?")    public void run() {        t.ceshi3();    }}

    ceshi3() 方法调用线程池配置,且异步执行。

    @Component@Slf4jpublic class ThreadTest {    /**     * 每10秒循环一次,一个线程共循环10次。     */    @Async("asyncTaskExecutor")    public void ceshi3() {        for (int i = 0; i <= 10; i  ) {            log.info("ceshi3: "   i);            try {                Thread.sleep(2000 * 5);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

    2、程序一启动就异步执行多线程

    通过继承 CommandLineRunner 类实现。

    @Componentpublic class ListennerTest implements CommandLineRunner {    @Autowired    private ThreadTest t;    @Override    public void run(String... args) {        for (int i = 1; i <= 10; i  ) {            t.ceshi();        }    }}
    @Component@Slf4jpublic class ThreadTest {    @Async("asyncTaskExecutor")    public void ceshi() {        log.info("ceshi");    }}

    3、定义一个 http 接口

    还可以通过接口的形式来异步调用多线程:

    @RestController@RequestMapping("thread")public class ListennerTest2 {    @Autowired    private ThreadTest t;    @GetMapping("ceshi2")    public void run() {        for (int i = 1; i < 10; i  ) {            t.ceshi2();        }    }}
    @Component@Slf4jpublic class ThreadTest {    @Async("asyncTaskExecutor")    public void ceshi2() {        for (int i = 0; i <= 3; i  ) {            log.info("ceshi2");        }    }}

    4、测试类

    @RunWith(SpringRunner.class)@SpringBootTestpublic class ThreadRunTest {    @Autowired    private ThreadTest t;    @Test    public void thread1() {        for (int i = 1; i <= 10; i  ) {            t.ceshi4();        }    }}
    @Component@Slf4jpublic class ThreadTest {    @Async("asyncTaskExecutor")    public void ceshi4() {        log.info("ceshi4");    }}

    关于"Spring中怎么使用ThreadPoolTaskExecutor自定义线程池及异步调用"这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对"Spring中怎么使用ThreadPoolTaskExecutor自定义线程池及异步调用"知识都有一定的了解,大家如果还想学习更多知识,欢迎关注行业资讯频道。

    线程 任务 配置 队列 核心 文件 注解 方法 最大 属性 目录 时间 知识 空闲 策略 循环 大小 程序 处理 内容 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 吉林农业资源信息大数据库 网络安全s盒的工作原理 网络安全周成都 提高数据库性能的方法 数据库技术和支持平台基本概念 学校买数据库价格 网络安全监测措施有哪些 徐州房管局网络安全设备 用什么技术开发数据库 做好高校网络安全工作 福建省中职计算机网络技术卷子 望城软件开发工程师 关于网络安全的例案例 服务器操作系统更换标书 软件开发做一个管理系统 正常一套软件开发到上线流程 株洲游戏软件开发招聘网 手机应用软件开发情况是啥意思 t3系统管理如何添加服务器 广州微交易软件开发 哪些情况会危害网络安全 数据库技术和支持平台基本概念 数据库双等号 scrum嵌入式软件开发 虹口区方便软件开发包括什么 淄博办公自动化软件开发公司 文件服务器参数 大同口碑好的网络安全服务保障 安卓系统用什么软件开发 怎样获得数据库元数据
    0