使用spring boot的@Async实现异步调用和线程池复用

简介: 使用spring boot的@Async实现异步调用和线程池复用


前言

我们在开发中经常会对执行慢的方法或不涉及主业务的方法执行异步调用。

  • 实现方式1: 可以自己手动创建原生的线程,实现异步。
  • 实现方式2:也可以使用springboot的@Async实现异步。

本文主要说下springboot的@Async方式和多线程时如何自定义线程池,以及希望有返回结果时,如何用其内置的方法为我所用。

1.@Async异步调用

异步的原理: springboot会为代理对象创建一个线程,执行异步方法。

  • 1.在springboot的入口函数处引入 开启异步自动配置注解@EnableAsync

  • 2书写异步方法

  • 3.调用
    在需要用到异步调用的地方,调用异步方法

特别注意

异步方法不可和调用它的类在一个类中,

因为@Async是springboot使用的代理对象来创建或者使用线程池中的线程处理,

如果在一个类中就成同步了。这点和不能调用同一个类的带有@Transactional方法是一个道理。

2.手动创建线程池管理@Async异步的线程

如果不手动创建线程池,springboot会用自己的线程池来处理,不过为了更好的控制线程,我们可以手动创建。springboot也给我们提供了配置方法。

我们可以这样配置。

@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {
    /**
     * 核心线程数(默认线程数)
     */
    private static final int corePoolSize = 10;
    /**
     * 最大线程数
     */
    private static final int maxPoolSize = 50;
    /**
     * 允许线程空闲时间(单位:默认为秒)
     */
    private static final int keepAliveTime = 5;
    /**
     * 缓冲队列大小
     */
    private static final int queueCapacity = 200;
    /**
     * 线程池名前缀
     */
    private static final String threadNamePrefix = "Async-Service-";
    @Bean("taskExecutor") // bean的名称,默认为首字母小写的方法名
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setKeepAliveSeconds(keepAliveTime);
        executor.setThreadNamePrefix(threadNamePrefix);
        // 线程池对拒绝任务的处理策略
        // CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 初始化
        executor.initialize();
        return executor;
    }
}

然后在用到的异步方法的地方,显式引用改线程池。

备注:

可以指定线程池的前缀threadNamePrefix,便于以后分析查看日志。如果需要创建多个线程池,指定不同的前缀,就可以快速定位出现问题的代码。

3.异步创建有返回值的调用

有时候我们不止希望异步执行任务,还希望任务执行完成后会有一个返回值,在java中提供了Future泛型接口,用来接收任务执行结果,springboot也提供了此类支持,使用实现了ListenableFuture接口的类如AsyncResult来作为返回值的载体。比如上例中,我们希望返回一个类型为String类型的值,可以将返回值改造为:

异步任务类

@Component  
public class AsyncTask {  
    @Async  
    public Future<String> task1() throws InterruptedException{  
        long currentTimeMillis = System.currentTimeMillis();  
        Thread.sleep(1000);  
        long currentTimeMillis1 = System.currentTimeMillis();  
        System.out.println("task1任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");  
        return new AsyncResult<String>("task1执行完毕");  
    }  
    @Async  
    public Future<String> task2() throws InterruptedException{  
        long currentTimeMillis = System.currentTimeMillis();  
        Thread.sleep(2000);  
        long currentTimeMillis1 = System.currentTimeMillis();  
        System.out.println("task2任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");  
        return new AsyncResult<String>("task2执行完毕");  
    }  
    @Async  
    public Future<String> task3() throws InterruptedException{  
        long currentTimeMillis = System.currentTimeMillis();  
        Thread.sleep(3000);  
        long currentTimeMillis1 = System.currentTimeMillis();  
        System.out.println("task3任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");  
        return new AsyncResult<String>("task3执行完毕");  
    }  
}

任务返回后使用:

@RequestMapping("")  
@RestController  
public class AsyncTaskController {  
    @Autowired  
    private AsyncTask asyncTask;  
    @RequestMapping("")  
    public String doTask() throws InterruptedException{  
        long currentTimeMillis = System.currentTimeMillis();  
        Future<String> task1 = asyncTask.task1();  
        Future<String> task2 = asyncTask.task2();  
        Future<String> task3 = asyncTask.task3();  
        String result = null;  
        for (;;) {  
            if(task1.isDone() && task2.isDone() && task3.isDone()) {  
                // 三个任务都调用完成,退出循环等待  
                break;  
            }  
            Thread.sleep(1000);  
        }  
        long currentTimeMillis1 = System.currentTimeMillis();  
        result = "task任务总耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms";  
        return result;  
    }  
}

大功告成,完!

相关文章
|
并行计算 Java 数据处理
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
867 0
|
算法 NoSQL Java
Springboot3新特性:GraalVM Native Image Support和虚拟线程(从入门到精通)
这篇文章介绍了Spring Boot 3中GraalVM Native Image Support的新特性,提供了将Spring Boot Web项目转换为可执行文件的步骤,并探讨了虚拟线程在Spring Boot中的使用,包括如何配置和启动虚拟线程支持。
816 9
Springboot3新特性:GraalVM Native Image Support和虚拟线程(从入门到精通)
|
Java Spring 容器
Spring使用异步注解@Async正确姿势
Spring使用异步注解@Async正确姿势,异步任务,spring boot
203 3
|
Java
SpringBoot线程问题
SpringBoot线程问题
124 0
|
Java Spring 容器
Spring boot 自定义ThreadPoolTaskExecutor 线程池并进行异步操作
Spring boot 自定义ThreadPoolTaskExecutor 线程池并进行异步操作
997 3
|
Java UED
基于SpringBoot自定义线程池实现多线程执行方法,以及多线程之间的协调和同步
这篇文章介绍了在SpringBoot项目中如何自定义线程池来实现多线程执行方法,并探讨了多线程之间的协调和同步问题,提供了相关的示例代码。
3775 0
|
Java 测试技术
Java SpringBoot Test 单元测试中包括多线程时,没跑完就结束了
Java SpringBoot Test 单元测试中包括多线程时,没跑完就结束了
292 0
|
30天前
|
JavaScript Java 关系型数据库
基于springboot的项目管理系统
本文探讨项目管理系统在现代企业中的应用与实现,分析其研究背景、意义及现状,阐述基于SSM、Java、MySQL和Vue等技术构建系统的关键方法,展现其在提升管理效率、协同水平与风险管控方面的价值。
|
1月前
|
搜索推荐 JavaScript Java
基于springboot的儿童家长教育能力提升学习系统
本系统聚焦儿童家长教育能力提升,针对家庭教育中理念混乱、时间不足、个性化服务缺失等问题,构建科学、系统、个性化的在线学习平台。融合Spring Boot、Vue等先进技术,整合优质教育资源,提供高效便捷的学习路径,助力家长掌握科学育儿方法,促进儿童全面健康发展,推动家庭和谐与社会进步。