• SpringBoot开启异步多线程


    前言:

            SpringBoot 的异步多线程需要从 java 的多线程基础说起,可以参考 java 多线程实现的三种方式区别。SpringBoot 在此基础上进行了多次封装,所以使用起来非常方便。

    一、核心参数说明

            ThreadPoolExecutor 是 java 的核心线程池类;Spring 对 ThreadPoolExecutor 进行二次封装形成了 ThreadPoolTaskExecutor,其中几个核心参数除了名字略有改动,核心含义没变,下面说明一下:

    • corePoolSize:核心线程池数
    • maxPoolSize:最大线程池数
    • queueCapacity:线程池队列最大容量
    • keepAliveSeconds:允许线程的空闲时间,核心线程外的线程在空闲时间到达后会被销毁
    • threadNamePrefix:线程池名的前缀
    • rejectedExecutionHandler:拒绝策略

    其中拒绝策略是线程达到某种饱和后的线程池的操作策略,总共四种:

    • AbortPolicy:如果线程池队列满了丢掉任务并且抛出RejectedExecutionException异常
    • DiscardPolicy:如果线程池队列满了,会直接丢掉这个任务并且不会抛出异常
    • DiscardOldestPolicy:如果队列满了,会将最早进入队列的任务删掉,再尝试加入队列
    • CallerRunsPolicy:如果添加到线程池失败,那么主线程会自己去执行该任务

    说明一下几个核心参数和拒绝策略是怎么工作的:

    1.核心线程数:m,最大线程数: n(一般 n > m),线程池队列最大容量: j,线程总数: s;

    2.线程进场后(s

    3.线程继续进场(m

    4.线程继续进场(m+j

    5.线程继续进场(s>n+j),此时触发拒绝策略;

    二、使用说明

      首先配置线程池:

    1. @Configuration
    2. @EnableAsync
    3. public class ThreadPoolConfig {
    4. @Bean("normalThreadPool") //线程池实例名,多个线程池配置需要声明,一个线程池可有可无
    5. public Executor executorNormal() {
    6. ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    7. executor.setCorePoolSize(3);
    8. executor.setMaxPoolSize(5);
    9. executor.setQueueCapacity(3);
    10. executor.setKeepAliveSeconds(60);
    11. executor.setThreadNamePrefix("NORMAL--");
    12. executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
    13. executor.initialize();
    14. return executor;
    15. }
    16. }

    其次在需要异步的方法上加 @Async 注解:

    1. @Slf4j
    2. @Service
    3. public class ThreadTaskService {
    4. @Async("normalThreadPool") //多个线程池配置时需指定配置实例
    5. public void task() {
    6. log.info("task start...");
    7. try {
    8. Thread.sleep(3000);
    9. } catch (InterruptedException e) {
    10. e.printStackTrace();
    11. }
    12. log.info("task end...");
    13. }
    14. }

    异步的调用跟普通 service 方法没区别:

    1. @Slf4j
    2. @RestController
    3. @RequestMapping("/thread")
    4. public class ThreadTaskController {
    5. @Autowired
    6. ThreadTaskService taskService;
    7. @GetMapping(value = "/start")
    8. public String getValue() {
    9. taskService.task();
    10. return "hello...";
    11. }
    12. }

    三、带返回值的异步

    要获取异步函数的返回值可以使用 Future,但是Future 的get方法是阻塞的,使用时需要注意。

    1. @Async("normalThreadPool")
    2. public CompletableFuture task() {
    3. String result = "000";
    4. log.info("task start...");
    5. try {
    6. Thread.sleep(5000);
    7. result = "333";
    8. } catch (InterruptedException e) {
    9. e.printStackTrace();
    10. }
    11. log.info("task end...");
    12. return CompletableFuture.completedFuture(result);
    13. }
    1. @Slf4j
    2. @RestController
    3. @RequestMapping("/thread")
    4. public class ThreadTaskController {
    5. @Autowired
    6. ThreadTaskService taskService;
    7. @GetMapping(value = "/start")
    8. public String getValue() throws ExecutionException, InterruptedException {
    9. CompletableFuture result = taskService.task();
    10. log.info("result:{}", result.get()); // get 方法会使主线程阻塞
    11. return "hello...";
    12. }
    13. }

    四、几种异步失败的情况

    1. 异步方法使用static关键词修饰;

    2. 缺少 @EnableAsync 注解;

    3. 同一个类中,一个方法调用另外一个有@Async注解的方法(原因是@Async注解的方法,是在代理类中执行的);

  • 相关阅读:
    Python Coroutine 池化实现
    Android 实现GIF播放的几种方式
    pat乙级1120(买地攻略) C++
    k8s快速入门教程-----8 secret and configmap
    Matlab绘图(2)通过代码进行局部放大绘图、多文件绘图
    道可云元宇宙每日资讯|安踏发布“羽龙元宇宙”时装秀
    多线程、多进程同时操作MMAP,会怎么样?
    mac安装+配置python3环境
    vue3前端调用后端接口实现批量删除
    OJ练习第26题——三数之和
  • 原文地址:https://blog.csdn.net/qingquanyingyue/article/details/126648820