SpringBoot从入门到放弃之注解@Async

1.概述

在使用@Async注解之前,首先需要了解同步调用和异步调用的区别。同步调用指的是一个方法内部可能有几个不同的方法,按照从上到下的顺序依次执行。如下图中代码所示,方法init中分别有method1()、method2()、method3()三个方法,这三个方法按照顺序依次执行。异步调用指的是init()方法中的method1()、method2()、method3()执行顺序无明确先后关系,也就是说加上@Async注解后,该方法就变为异步调用。该注解应用的场景主要为:(1)某一方法不需要立即执行,也不需要立即返回执行结果的;(2)该方法运行时间较长;(3)该方法针对的业务场景优先级低。本文将讲述该注解的简单使用方法。

void init() {
void method1();   //方法1
void method2();   //方法2
void method3();   //方法3
}

2.@Async注解的使用

2.1 启动类添加注解@EnableAsync

@EnableDiscoveryClient
@EnableAsync
@EnableFeignClients
@SpringBootApplication
public class StreamQualityProvider {
    public static void main(String[] args) {
        SpringApplication.run(StreamQualityProvider.class, args);
        System.setProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow", "|{}");
    }
}

2.2 方法上添加@Async注解

@Slf4j
@Service
public class AsyncServiceImpl implements AsyncService {

    @Async
    @Override
    public void method1() {
        log.info("方法执行---------,{}", "method1");
    }

    @Async
    @Override
    public void method2() {
        log.info("方法执行---------,{}", "method2");
    }

    @Async
    @Override
    public void method3() {
        log.info("方法执行---------,{}", "method3");
    }

    @Async
    @Override
    public String method4() {
        log.info("方法执行---------,{}", "method4");
        try {
            Thread.sleep(1000L);
        } catch (Exception e) {
            log.error("休眠异常:{}", e);
        }
        return "method4";
    }

    @Async
    @Override
    public String method5() {
        log.info("方法执行---------,{}", "method5");
        try {
            Thread.sleep(3000L);
        } catch (Exception e) {
            log.error("休眠异常:{}", e);
        }
        return "method5";
    }

    @Async
    @Override
    public String method6() {
        log.info("方法执行---------,{}", "method6");
        try {
            Thread.sleep(5000L);
        } catch (Exception e) {
            log.error("休眠异常:{}", e);
        }
        return "method6";
    }

    @Override
    public String method7() {
        method1();
        method2();
        method3();
        method4();
        return "success";
    }
}

2.3 测试

1.异步调用生效

 @GetMapping("/test")
    public String testMethod1() {
        service.method1();
        service.method2();
        service.method3();
        service.method4();
        service.method5();
        service.method6();
        return "success";
    }

结果为:
在这里插入图片描述
每次方法调用的顺序都可能不一样,是因为异步调用方法无明确先后执行顺序。

2.4 注意事项

1. 调用方法与被@Async修饰的方法不能在同一类中,若在同一类中,则异步方法不生效,会变成同步调用。
测试代码如下:

@PostMapping("/test1")
    public String testMethod2() {
        return service.method7();
    }

结果为:
在这里插入图片描述
2.调用方法不能以static关键字修饰,否则不生效。
在这里插入图片描述
测试如下:

 @Override
    public String method9() {
        method8();
        method10();
        method11();
        return "success";
    }

    @Async
    public static String method8() {
        log.info("方法执行---------,{}", "method8");
        try {
            Thread.sleep(5000L);
        } catch (Exception e) {
            log.error("休眠异常:{}", e);
        }
        return "method8";
    }

    @Async
    public static String method10() {
        log.info("方法执行---------,{}", "method10");
        try {
            Thread.sleep(5000L);
        } catch (Exception e) {
            log.error("休眠异常:{}", e);
        }
        return "method8";
    }

    @Async
    public static String method11() {
        log.info("方法执行---------,{}", "method11");
        try {
            Thread.sleep(5000L);
        } catch (Exception e) {
            log.error("休眠异常:{}", e);
        }
        return "method11";
    }
 @PostMapping("/test9")
    public String testMethod9() {
        return service.method9();
    }

结果为:
在这里插入图片描述

2.5 多异步方法执行

测试方案如下:

 @Async
    public Future<String> taskOne() throws Exception {
        System.out.println("任务一");
        try {
            Thread.sleep(1000L);
            log.info("线程休眠1s");
        }catch (Exception e) {
            log.error("Exception:{}",e);
        }
        return new AsyncResult<>("任务一执行完成");

    }

    @Async
    public Future<String> taskTwo() throws Exception {
        try {
            Thread.sleep(1000L);
            log.info("线程休眠1s");
        }catch (Exception e) {
            log.error("Exception:{}",e);
        }
        System.out.println("任务二");
        return new AsyncResult<>("任务二执行完成");
    }

    @Async
    public Future<String> taskThree() throws Exception {
        try {
            Thread.sleep(1000L);
            log.info("线程休眠1s");
        }catch (Exception e) {
            log.error("Exception:{}",e);
        }
        System.out.println("任务三");
        return new AsyncResult<>("任务三执行完成");
    }
@PostMapping("/test10")
    public String testMethod10() throws Exception {
        long startTime = System.currentTimeMillis();
        Future<String> taskOne = service.taskOne();
        Future<String> taskTwo = service.taskTwo();
        Future<String> taskThree = service.taskThree();
        int count = 0;
        while (true) {
            if (taskOne.isDone() && taskTwo.isDone() && taskThree.isDone()) {
                log.info("success");
                break;
            }
            try {
                Thread.sleep(100L);
                log.info("设备休眠:{}次", count++);
            } catch (Exception e) {
                log.error("Exception:{}", e);
            }
        }
        long endTime = System.currentTimeMillis();
        long totalTime = endTime - startTime;
        log.info("方法执行时间为:{}",totalTime);
        return "方法执行时间为:" + totalTime;
    }

结果为:
在这里插入图片描述
多异步执行时,这里测试的是三个任务全部执行完成后计算总时间。Future接口可以在异步任务执行完成后返回一个回调函数,只要设置回调信息,就能够判断任务是否正确执行,对于异步函数,添加 Future 返回值类型,使用 new AsyncResult<>() 设置回调信息。当多个方法执行完成拿到结果之后,可以完成接下来的业务逻辑,像上述三个异步方法isDone()都为true之后,可以实现接下来的业务逻辑。

2.6 自定义线程池运行异步方法

@Configuration
public class AsyncConfiguration {
    @Bean("asyncExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10); // 线程池创建时候初始化的线程数
        executor.setMaxPoolSize(20); // 线程池最大的线程数,只有在缓冲队列满了之后,才会申请超过核心线程数的线程
        executor.setQueueCapacity(200); // 缓冲任务队列的大小
        executor.setKeepAliveSeconds(60); // 允许线程的空闲时间,超过会被销毁
        executor.setThreadNamePrefix("custom-prefix-");// 线程的前缀
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 对拒绝任务的处理策略
        return executor;
    }
}

在这里插入图片描述
结果为:
在这里插入图片描述

3.小结

1.@Async修饰的方法与调用方法不能在同一类中;
2.@Async不能使用在以static关键字修饰的方法上;
3.多异步执行可以定义Future方法来获取执行结果;
4.@Async执行方法的线程可进行自定义设置。

4.参考文献

1.https://juejin.cn/post/6910756747351162894
2.https://juejin.cn/post/6864818743965892622
3.https://juejin.cn/post/6907872876171526157

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值