当前位置:   article > 正文

Springboot利用CompletableFuture异步执行线程(有回调和无回调)_completablefuture.runasync

completablefuture.runasync

目录

背景

实现

一、异步线程配置类

 二、自定义异步异常统一处理类

三、实现调用异步(无回调-runAsync())

四、实现调用异步(有回调-supplyAsync())  

五、异步执行错误异常示例

背景

项目中总会有需要异步执行来避免浪费时间资源的情况,这就需要异步操作。异步又分两种:

1、无回调:有一些执行过程对用户而言不需要反馈回调,只需要自己执行即可,且执行过程时间较长(某些第三方接口,如发送短信验证码、查取ip属地等等),如果同步执行,势必会影响到用户体验,这时候就可以使用CompletableFuture.runAsync()方法了。

2、有回调:在执行异步操作结束后,需要获得异步方法返回的值,然后再回调给用户展示,这时候就需要用到CompletableFuture.supplyAsync()方法了。

实现

一、异步线程配置类

  1. /**
  2. * 异步线程配置类
  3. */
  4. @EnableAsync
  5. @Configuration
  6. public class AsyncConfig implements AsyncConfigurer {
  7. @Override
  8. public Executor getAsyncExecutor() {
  9. ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  10. // 设置核心线程数
  11. executor.setCorePoolSize(8);
  12. // 设置最大线程数
  13. executor.setMaxPoolSize(20);
  14. // 设置队列大小
  15. executor.setQueueCapacity(Integer.MAX_VALUE);
  16. // 设置线程活跃时间(秒)
  17. executor.setKeepAliveSeconds(60);
  18. // 设置线程名前缀+分组名称
  19. executor.setThreadNamePrefix("AsyncOperationThread-");
  20. executor.setThreadGroupName("AsyncOperationGroup");
  21. // 所有任务结束后关闭线程池
  22. executor.setWaitForTasksToCompleteOnShutdown(true);
  23. // 初始化
  24. executor.initialize();
  25. return executor;
  26. }
  27. /**
  28. * 自定义异步异常
  29. * @return
  30. */
  31. @Override
  32. public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
  33. return new AsyncException();
  34. }
  35. }

 二、自定义异步异常统一处理类

  1. /**
  2. * 异步请求异常错误
  3. */
  4. public class AsyncException implements AsyncUncaughtExceptionHandler {
  5. @Override
  6. public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
  7. System.out.println("--------------------异步请求异常捕获---------------------------------");
  8. System.out.println("Exception message - " + throwable.getMessage());
  9. System.out.println("Method name - " + method.getName());
  10. for (Object param : obj) {
  11. System.out.println("Parameter value - " + param);
  12. }
  13. System.out.println("---------------------异步请求异常捕获---------------------------------");
  14. }
  15. }

三、实现调用异步(无回调-runAsync()

在需要异步的方法前加上@Async再调用即可如下,在service层中新建一个方法,模拟第三方接口请求延时3秒(异步方法和调用方法一定要写在不同的类中 ,如果写在一个类中,是没有效果的!)

  1. @Service
  2. public class LoginLogServiceImpl extends ServiceImpl<LoginLogMapper, LoginLog> implements ILoginLogService {
  3. // 无回调
  4. @Async
  5. public CompletableFuture<Void> test() {
  6. return CompletableFuture.runAsync(() -> {
  7. // 模拟第三方请求加载时间
  8. Thread.sleep(3000);
  9. System.out.println("保存成功登录日志");
  10. });
  11. }
  12. }

 然后在controller层中调用该方法,

  1. @PostMapping("/login")
  2. public Result login() throws InterruptedException {
  3. System.out.println("登录验证成功");
  4. // 异步操作
  5. iLoginLogService.test();
  6. System.out.println("登录接口请求返回用户成功");
  7. // 正常返回结果
  8. return Result.success(200, "登录成功");
  9. }

执行完成后,我们打开控制台,可以看到, 异步请求在接口执行正常,在接口返回结果后执行完成。

四、实现调用异步(有回调-supplyAsync())  

这里我们多采用3个异步线程来模拟实际效果,且延迟时间为1 6 3,看下他们的异步执行输出顺序是否和我们模拟的执行时间相同,如下:

  1. @Service
  2. public class LoginLogServiceImpl extends ServiceImpl<LoginLogMapper, LoginLog> implements ILoginLogService {
  3. // 有返回值的异步方法CompletableFuture.supplyAsync()
  4. public CompletableFuture<String> test1() {
  5. return CompletableFuture.supplyAsync(() -> {
  6. ThreadUtil.sleep(1000);
  7. System.out.println("test1异步请求结果有返回");
  8. return "aaa";
  9. });
  10. }
  11. public CompletableFuture<String> test2() {
  12. return CompletableFuture.supplyAsync(() -> {
  13. ThreadUtil.sleep(6000);
  14. System.out.println("test2异步请求结果有返回");
  15. return "bbb";
  16. });
  17. }
  18. public CompletableFuture<String> test3() {
  19. return CompletableFuture.supplyAsync(() -> {
  20. ThreadUtil.sleep(3000);
  21. System.out.println("test3异步请求结果有返回");
  22. return "ccc";
  23. });
  24. }
  25. }

 在controller层中利用CompletableFuture的get()方法获取数据,但是要加上try/catch捕获异常

  1. @PostMapping("/login")
  2. public Result login() {
  3. System.out.println("登录验证成功");
  4. // 异步保存登录成功日志
  5. CompletableFuture<String> future1 = iLoginLogService.test1();
  6. CompletableFuture<String> future2 = iLoginLogService.test2();
  7. CompletableFuture<String> future3 = iLoginLogService.test3();
  8. System.out.println("模拟正常执行的方法");
  9. String result1 = "111";
  10. String result2 = "222";
  11. String result3 = "333";
  12. String result = "";
  13. // 需要用try/catch来获取异步返回值
  14. try {
  15. // get()方法获取返回值,并设置10秒超时就放弃,直接报错
  16. result1 = future1.get(10, TimeUnit.SECONDS);
  17. result2 = future2.get(10, TimeUnit.SECONDS);
  18. result3 = future3.get(10, TimeUnit.SECONDS);
  19. System.out.println("返回结果为:" + result1);
  20. System.out.println("返回结果为:" + result2);
  21. System.out.println("返回结果为:" + result3);
  22. result = result1 + result2 + result3;
  23. // 正常返回结果
  24. return Result.success(200, "登录成功", result);
  25. } catch (Exception e) {
  26. System.out.println(e);
  27. return Result.success(501, "登录失败,异常错误");
  28. }
  29. }

 结果可以看到如下图所示,异步线程同时执行,但最后返回结果是等所有线程执行完成后,再返回,这就是有回调的异步操作:

五、异步执行错误异常示例

  1. // 无回调
  2. @Async
  3. public CompletableFuture<Void> test() {
  4. return CompletableFuture.runAsync(() -> {
  5. // 模拟第三方请求加载时间
  6. Thread.sleep(1000);
  7. // 异常错误模拟
  8. Integer test = 1/0;
  9. System.out.println("异步请求开始执行完成");
  10. });
  11. }

 结果就是完全不影响接口执行,且打印出报错信息,如下:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/空白诗007/article/detail/824150
推荐阅读
相关标签
  

闽ICP备14008679号