Springboot 异步调用

墨蓝 2021-07-24 20:58 650阅读 0赞

当我们业务中需要执行一个比较耗时的任务并且不影响主题程序的运行,那就需要异步了

首先,配置springboot框架整体的线程池:

  1. package pers.zc.activiti.config;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.scheduling.annotation.AsyncConfigurer;
  4. import org.springframework.scheduling.annotation.EnableAsync;
  5. import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
  6. import java.util.concurrent.Executor;
  7. import java.util.concurrent.ThreadPoolExecutor;
  8. @Configuration
  9. @EnableAsync
  10. public class AsyncExecutorConfig implements AsyncConfigurer {
  11. /**
  12. * 线程池维护线程的最小数量
  13. */
  14. private int corePoolSize =2;
  15. /**
  16. * 线程池维护线程的最大数量
  17. */
  18. private int maxPoolSize =2;
  19. /**
  20. * 队列最大长度
  21. */
  22. private int queueCapacity = 100;
  23. /**
  24. * 获取异步线程池执行对象
  25. */
  26. @Override
  27. public Executor getAsyncExecutor() {
  28. ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
  29. taskExecutor.setCorePoolSize(corePoolSize);
  30. taskExecutor.setMaxPoolSize(maxPoolSize);
  31. taskExecutor.setQueueCapacity(queueCapacity);
  32. //用于调试
  33. taskExecutor.setThreadNamePrefix("ThreadPool-");
  34. //用于传入请求范围上下文
  35. taskExecutor.setTaskDecorator(new ContextCopyingDecorator());
  36. taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
  37. //拒绝策略 CallerRunsPolicy 由调用线程处理该任务
  38. taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
  39. taskExecutor.initialize();
  40. return taskExecutor;
  41. }
  42. }

当然 你也可以在 springboot启动类上添加一个注解 @EnableAsync , 也可以不需要以上的配置,只是那样没有限制,不建议使用,虽然很方便.下面实例代码:

  1. package pers.zc.activiti;
  2. import org.mybatis.spring.annotation.MapperScan;
  3. import org.springframework.boot.SpringApplication;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5. import org.springframework.scheduling.annotation.EnableAsync;
  6. @SpringBootApplication
  7. @EnableAsync //如果要用springboot的异步,在这里加此注解,
  8. public class Application {
  9. public static void main(String[] args) {
  10. SpringApplication.run(Application.class, args);
  11. }
  12. }

上面两种方式二选一即可,

下面是异步方法

  1. package pers.zc.activiti.service.impl;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.scheduling.annotation.Async;
  4. import org.springframework.scheduling.annotation.AsyncResult;
  5. import org.springframework.stereotype.Service;
  6. import pers.zc.activiti.service.AsyncService;
  7. import pers.zc.activiti.service.TestService;
  8. import java.util.concurrent.Future;
  9. /**
  10. * @Annotion:
  11. * @ClassName: AsyncServiceImpl
  12. * @Author:
  13. * @Date: 2019/10/11 14:19
  14. * @Version: 1.0
  15. */
  16. @Service
  17. public class AsyncServiceImpl {
  18. // 这是有返回值的方法
  19. @Async
  20. public Future<String> run1() {
  21. try {
  22. Thread.sleep(5000);
  23. System.out.println("run1运行了");
  24. } catch (Exception e) {
  25. e.printStackTrace();
  26. }
  27. return new AsyncResult<String> ("run");
  28. }
  29. //无返回值的方法
  30. @Async
  31. public void run2() {
  32. try {
  33. Thread.sleep(5000);
  34. System.out.println("run2运行了");
  35. } catch (Exception e) {
  36. e.printStackTrace();
  37. }
  38. }
  39. }

上面类上加@Service 和@Component 注解是一样的效果,都会将该类在springboot启动的时候加载到springboot 容器中,

方法上加@Async注解 难么该方法就是异步的,会自然使用springboot的全局线程池配置,如果将@Async注解加到类上,那么该类就是异步的.

  1. @Controller
  2. @RequestMapping("/async")
  3. public class AsyncController {
  4. @Autowired
  5. private AsyncServiceImpl asyncService;
  6. @GetMapping("test1")
  7. public void test() throws ExecutionException, InterruptedException {
  8. //在这里直接调用加有@Async注解的方法 即是异步执行的,
  9. //run1是有返回值的,如果要获取返回值,那么该异步也变相变成同步方法,
  10. Future<String> s = asyncService.run1();
  11. System.out.println(s.get());//通过.get()方法即可获取异步方法中的返回值.
  12. asyncService.run2();
  13. System.out.println("这里首先打印");
  14. }
  15. }

请求上面Controller层接口,查看控制台打印内容, 可以发现先后顺序.,上面异步方法会自动使用最前面配置的线程池里面的线程,自行打印线程名称即可,对于有返回值的方法, 如果不获取返回内容,方法是异步的, 如果要获取返回值,那么线程将会阻塞到有返回值为止,也跟同步没啥区别了.

如果想要获取线程池,然后将自己创建的线程放入线程池中跑,那么往下看

  1. @Controller
  2. @RequestMapping("/async")
  3. public class AsyncController {
  4. @Autowired
  5. private AsyncExecutorConfig asyncExecutorConfig;
  6. @GetMapping("test2")
  7. public void test() throws ExecutionException, InterruptedException {
  8. long time1 = System.currentTimeMillis();
  9. /*获取线程池*/
  10. ThreadPoolTaskExecutor asyncExecutor = (ThreadPoolTaskExecutor) asyncExecutorConfig.getAsyncExecutor();
  11. //自己创建一个线程
  12. Thread thread = new Thread(new Runnable() {
  13. @Override
  14. public void run() {
  15. try {
  16. Thread.sleep(5000);
  17. } catch (InterruptedException e) {
  18. e.printStackTrace();
  19. }
  20. //输出线程名称
  21. System.out.println("************"+Thread.currentThread().getName());
  22. }
  23. });
  24. //将创建的线程放入线程池中
  25. asyncExecutor.submit(thread);
  26. //打印线程名称
  27. long time2 = System.currentTimeMillis();
  28. System.out.println("用时:"+(time2-time1));
  29. }

自己写个springboot的demo项目, 自己创建一个线程池可以试试

发表评论

表情:
评论列表 (有 0 条评论,650人围观)

还没有评论,来说两句吧...

相关阅读

    相关 SpringBoot异步调用

    除了异步请求,一般上我们用的比较多的应该是异步调用。通常在开发过程中,会遇到一个方法是和实际业务无关的,没有紧密性的。比如记录日志信息等业务。这个时候正常就是启一个新线程去做一

    相关 Springboot 异步调用

    当我们业务中需要执行一个比较耗时的任务并且不影响主题程序的运行,那就需要异步了 首先,配置springboot框架整体的线程池: package pers.zc.a