• springboot @Scheduled和@Async


    原文 springboot中@Scheduled 和@Async的使用_huangyaa729的博客-CSDN博客_@async @scheduled

    首先,需要了解@Scheduled 和@Async这俩注解的区别:

    @Scheduled 任务调度注解,主要用于配置定时任务;springboot默认的调度器线程池大小为 1。

    @Async 任务异步执行注解,主要用于方法上,表示当前方法会使用新线程异步执行;springboot默认执行器线程池大小为100。

    具体可参考源码和这两篇博客(里面有些描述感觉有点问题,但不影响核心思想的表达,大家可参考看下):
    https://blog.csdn.net/hello__ZC/article/details/102455847
    https://blog.csdn.net/weixin_34319374/article/details/88811716

    所以,如果在使用springboot定时器时,如果有多个定时任务时,在使用默认的调度器配置,就会出现排队现象,因为同时只能有一个任务在执行,这个时候当一个任务挂死,那后面的定时任务就不能有效执行了;
    解决办法就是自定义调度器,有两种方式:
    方法一:

    1. @Bean
    2. public TaskScheduler scheduledExecutorService() {
    3. ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
    4. scheduler.setPoolSize(10);
    5. scheduler.setThreadNamePrefix("scheduled-thread-");
    6. //设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
    7. scheduler.setWaitForTasksToCompleteOnShutdown(true);
    8. //设置线程池中任务的等待时间,如果超过这个时候还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住
    9. scheduler.setAwaitTerminationSeconds(60);
    10. //这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务
    11. scheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    12. return scheduler;
    13. }

    方法二:

    1. @Configuration
    2. public class ScheduledConfig implements SchedulingConfigurer {
    3. public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
    4. taskRegistrar.setScheduler(setExecutor());
    5. }
    6. @Bean(destroyMethod="shutdown")
    7. public Executor setExecutor(){
    8. return Executors.newScheduledThreadPool(10); // 10个线程来处理。
    9. }
    10. }

    上述自定义调度器的方式,会有一个问题:当有足够的空余线程时,多任务时并行执行,但是同一定时任务仍会同步执行(当定时任务的执行时间大于每次执行的时间间隔时即可发现);

    配合@Async 注解使用,这样在每次执行定时任务时就新开一个线程,异步非阻塞运行;同时使用这两个注解的效果,相当于@Scheduled仅仅负责调度,而@Async指定的executor负责任务执行,不再使用调度器中的执行器来执行任务(由实际测试结果来猜测的,并没有找到对应的源码逻辑,待后续补充)。
    自定义执行器配置如下:

    1. @Bean("taskExecutor")
    2. public ThreadPoolTaskExecutor taskExecutor(){
    3. ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    4. executor.setCorePoolSize(corePoolSize);
    5. executor.setMaxPoolSize(maxPoolSize);
    6. executor.setQueueCapacity(queueCapacity);
    7. executor.setKeepAliveSeconds(keepAliveTime);
    8. executor.setThreadNamePrefix(threadNamePrefix);
    9. // 线程池对拒绝任务的处理策略
    10. executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    11. // 初始化
    12. executor.initialize();
    13. return executor;
    14. }

    其余注意事项,可参考如下两份博客:
    https://blog.csdn.net/hello__ZC/article/details/102455847
    https://blog.csdn.net/dviewer/article/details/78022865 

  • 相关阅读:
    CGO 初步认知和基本数据类型转换
    浅挖一下String类,解决String常见的面试题
    V90伺服驱动器控制(PN版本)
    鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:Refresh)
    认识 URL
    点餐小程序实战教程06-首页开发
    LLm微调使用的数据集
    高速电路逻辑电平转换设计
    山西电力市场日前价格预测【2023-09-22】
    《自然语言处理实战:利用Python理解、分析和生成文本》读书笔记:第1章 NLP概述
  • 原文地址:https://blog.csdn.net/Coco_chun/article/details/126400401