博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring Boot使用@Async实现异步调用
阅读量:4560 次
发布时间:2019-06-08

本文共 7174 字,大约阅读时间需要 23 分钟。

异步调用对应的是同步调用,同步调用可以理解为按照定义的顺序依次执行,有序性;异步调用在执行的时候不需要等待上一个指令调用结束就可以继续执行。

我们将在创建一个 Spring Boot 工程来说明。具体工程可以参考github代码 async模块

pom 依赖如下:

org.springframework.boot
spring-boot-starter
spring-boot-starter-logging
org.springframework.boot
ch.qos.logback
logback-access
ch.qos.logback
logback-classic
ch.qos.logback
logback-core
org.springframework.boot
spring-boot-starter-aop
org.projectlombok
lombok
1.18.2
true
org.springframework.boot
spring-boot-starter-test
test

启动类如下:

@SpringBootApplicationpublic class AsyncApplication {    public static void main(String[] args) {        SpringApplication.run(AsyncApplication.class, args);    }}

定义线程池

import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.scheduling.annotation.EnableAsync;import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.Executor;import java.util.concurrent.ThreadPoolExecutor;/** * 异步线程池 */@Configuration@EnableAsyncpublic class AsyncExecutorConfig {    /**     * Set the ThreadPoolExecutor's core pool size.     */    private int corePoolSize = 8;    /**     * Set the ThreadPoolExecutor's maximum pool size.     */    private int maxPoolSize = 16;    /**     * Set the capacity for the ThreadPoolExecutor's BlockingQueue.     */    private int queueCapacity = 200;    private String threadNamePrefix = "AsyncExecutor-";    @Bean("taskExecutor")    public Executor taskExecutor() {        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();        executor.setCorePoolSize(corePoolSize);        executor.setMaxPoolSize(maxPoolSize);        executor.setQueueCapacity(queueCapacity);        executor.setKeepAliveSeconds(60);        executor.setThreadNamePrefix(threadNamePrefix);        // rejection-policy:当pool已经达到max size的时候,如何处理新任务          // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行          executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());        executor.initialize();        return executor;    }}

代码中我们通过 ThreadPoolTaskExecutor 创建了一个线程池。参数含义如下所示:

  • corePoolSize:线程池创建的核心线程数
  • maxPoolSize:线程池最大线程池数量,当任务数超过corePoolSize以及缓冲队列也满了以后才会申请的线程数量。
  • setKeepAliveSeconds: 允许线程空闲时间60秒,当maxPoolSize的线程在空闲时间到达的时候销毁。
  • ThreadNamePrefix:线程的前缀任务名字。
  • RejectedExecutionHandler:当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务

使用实战

@Slf4j@Servicepublic class OrderService {    public static Random random = new Random();    @Autowired    private AsyncTask asyncTask;    public void doShop() {        try {            createOrder();            // 调用有结果返回的异步任务            Future
pay = asyncTask.pay(); if (pay.isDone()) { try { String result = pay.get(); log.info("异步任务返回结果{}", result); } catch (ExecutionException e) { e.printStackTrace(); } asyncTask.vip(); asyncTask.sendSms(); } otherJob(); } catch (InterruptedException e) { log.error("异常", e); } } public void createOrder() { log.info("开始做任务1:下单成功"); } /** * 错误使用,不会异步执行:调用方与被调方不能在同一个类。主要是使用了动态代理,同一个类的时候直接调用,不是通过生成的动态代理类调用 */ @Async("taskExecutor") public void otherJob() { log.info("开始做任务4:物流"); long start = System.currentTimeMillis(); try { Thread.sleep(random.nextInt(10000)); } catch (InterruptedException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); log.info("完成任务4,耗时:" + (end - start) + "毫秒"); }}

异步任务服务类

mport lombok.extern.slf4j.Slf4j;import org.springframework.scheduling.annotation.Async;import org.springframework.scheduling.annotation.AsyncResult;import org.springframework.stereotype.Component;import java.util.Random;import java.util.concurrent.Future;@Component@Slf4jpublic class AsyncTask {    public static Random random = new Random();    @Async("taskExecutor")    public void sendSms() throws InterruptedException {        log.info("开始做任务2:发送短信");        long start = System.currentTimeMillis();        Thread.sleep(random.nextInt(10000));        long end = System.currentTimeMillis();        log.info("完成任务1,耗时:" + (end - start) + "毫秒");    }    @Async("taskExecutor")    public Future
pay() throws InterruptedException { log.info("开始做异步返回结果任务2:支付"); long start = System.currentTimeMillis(); Thread.sleep(random.nextInt(10000)); long end = System.currentTimeMillis(); log.info("完成任务2,耗时:" + (end - start) + "毫秒"); return new AsyncResult<>("会员服务完成"); } /** * 返回结果的异步调用 * @throws InterruptedException */ @Async("taskExecutor") public void vip() throws InterruptedException { log.info("开始做任务5:会员"); long start = System.currentTimeMillis(); Thread.sleep(random.nextInt(10000)); long end = System.currentTimeMillis(); log.info("开始做异步返回结果任务5,耗时:" + (end - start) + "毫秒"); }}

单元测试

@RunWith(SpringRunner.class)@SpringBootTest(classes = AsyncApplication.class)public class AsyncApplicationTests {    @Autowired    private OrderService orderService;    @Test    public void testAsync() {        orderService.doShop();        try {            Thread.currentThread().join();        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

结果展示

2019-05-16 20:25:06.577 [INFO ] [main] - zero.springboot.study.async.service.OrderService-52 开始做任务1:下单成功 2019-05-16 20:25:06.586 [INFO ] [main] - zero.springboot.study.async.service.OrderService-60 开始做任务4:物流 2019-05-16 20:25:06.599 [INFO ] [AsyncExecutor-1] - zero.springboot.study.async.service.AsyncTask-38 开始做异步返回结果任务2:支付 2019-05-16 20:25:13.382 [INFO ] [AsyncExecutor-1] - zero.springboot.study.async.service.AsyncTask-42 完成任务2,耗时:6783毫秒 2019-05-16 20:25:14.771 [INFO ] [main] - zero.springboot.study.async.service.OrderService-68 完成任务4,耗时:8184毫秒

可以看到有的线程的名字就是我们线程池定义的前缀,说明使用了线程池异步执行。其中我们示范了一个错误的使用案例 otherJob(),并没有异步执行。

原因:

spring 在扫描bean的时候会扫描方法上是否包含@Async注解,如果包含,spring会为这个bean动态地生成一个子类(即代理类,proxy),代理类是继承原来那个bean的。此时,当这个有注解的方法被调用的时候,实际上是由代理类来调用的,代理类在调用时增加异步作用。然而,如果这个有注解的方法是被同一个类中的其他方法调用的,那么该方法的调用并没有通过代理类,而是直接通过原来的那个 bean 也就是 this. method,所以就没有增加异步作用,我们看到的现象就是@Async注解无效。

关注公众号获取最新文章一起进步JavaStorm.png

转载于:https://www.cnblogs.com/uniqueDong/p/10944632.html

你可能感兴趣的文章
retinex相关代码汇总
查看>>
Cortex-M3 异常返回值EXC_RETURN
查看>>
kettle 转换字段遇到问题(couldn't get row from result set)——摘
查看>>
nginx首页根据IP跳转
查看>>
【2019-08-20】有点目标,有点计划,有点目的
查看>>
【2019-09-10】美,真的跟年龄无关
查看>>
【2019-09-28】少,但更好
查看>>
【2019-09-13】耐心观察是一种技能
查看>>
mysql数据库2-常用命令
查看>>
安卓开发环境搭建(转)
查看>>
英语学习一周年
查看>>
set容器
查看>>
python基础学习目录
查看>>
微软必应地图加载错误:Uncaught TypeError: Microsoft.Maps.Location is not a constructor
查看>>
卷积神经网络是如何工作的(译文)
查看>>
微信开发 笔记1
查看>>
SQL server 删除日志文件 秒删
查看>>
MethodChannel 实现flutter 与 原生通信
查看>>
lua的性能优化
查看>>
vs2012 出现断点无法命中 解决方案。
查看>>