zoukankan      html  css  js  c++  java
  • SpringBoot中使用@scheduled定时执行任务需要注意的坑

    要注意什么坑
    不绕弯子了,直接说这个坑是啥:

    SpringBoot使用@scheduled定时执行任务的时候是在一个单线程中,如果有多个任务,其中一个任务执行时间过长,则有可能会导致其他后续任务被阻塞直到该任务执行完成。也就是会造成一些任务无法定时执行的错觉

    可以通过如下代码进行测试:

    @Scheduled(cron = "0/1 * * * * ? ")
        public void deleteFile() throws InterruptedException {
            log.info("111delete success, time:" + new Date().toString());
            Thread.sleep(1000 * 5);//模拟长时间执行,比如IO操作,http请求
        }
    
        @Scheduled(cron = "0/1 * * * * ? ")
        public void syncFile() {
            log.info("222sync success, time:" + new Date().toString());
        }
        
    /**输出如下:
    [pool-1-thread-1] : 111delete success, time:Mon Nov 26 20:42:13 CST 2018
    [pool-1-thread-1] : 222sync success, time:Mon Nov 26 20:42:18 CST 2018
    [pool-1-thread-1] : 111delete success, time:Mon Nov 26 20:42:19 CST 2018
    [pool-1-thread-1] : 222sync success, time:Mon Nov 26 20:42:24 CST 2018
    [pool-1-thread-1] : 222sync success, time:Mon Nov 26 20:42:25 CST 2018
    [pool-1-thread-1] : 111delete success, time:Mon Nov 26 20:42:25 CST 2018
    上面的日志中可以明显的看到syncFile被阻塞了,直达deleteFile执行完它才执行了
    而且从日志信息中也可以看出@Scheduled是使用了一个线程池中的一个单线程来执行所有任务的。
    **/
    
    /**如果把Thread.sleep(1000*5)注释了,输出如下:
    [pool-1-thread-1]: 111delete success, time:Mon Nov 26 20:48:04 CST 2018
    [pool-1-thread-1]: 222sync success, time:Mon Nov 26 20:48:04 CST 2018
    [pool-1-thread-1]: 222sync success, time:Mon Nov 26 20:48:05 CST 2018
    [pool-1-thread-1]: 111delete success, time:Mon Nov 26 20:48:05 CST 2018
    [pool-1-thread-1]: 111delete success, time:Mon Nov 26 20:48:06 CST 2018
    [pool-1-thread-1]: 222sync success, time:Mon Nov 26 20:48:06 CST 2018
    这下正常了
    **/

    解决办法

    1.将@Scheduled注释的方法内部改成异步执行
    如下:

    //当然了,构建一个合理的线程池也是一个关键,否则提交的任务也会在自己构建的线程池中阻塞
        ExecutorService service = Executors.newFixedThreadPool(5);
    
        @Scheduled(cron = "0/1 * * * * ? ")
        public void deleteFile() {
            service.execute(() -> {
                log.info("111delete success, time:" + new Date().toString());
                try {
                    Thread.sleep(1000 * 5);//改成异步执行后,就算你再耗时也不会印象到后续任务的定时调度了
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
    
        @Scheduled(cron = "0/1 * * * * ? ")
        public void syncFile() {
            service.execute(()->{
                log.info("222sync success, time:" + new Date().toString());
            });
        }

    2.把Scheduled配置成成多线程执行

    @Configuration
    public class ScheduleConfig implements SchedulingConfigurer {
        @Override
        public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
            //当然了,这里设置的线程池是corePoolSize也是很关键了,自己根据业务需求设定
            taskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
            
            
            /**为什么这么说呢?
            假设你有4个任务需要每隔1秒执行,而其中三个都是比较耗时的操作可能需要10多秒,而你上面的语句是这样写的:
            taskRegistrar.setScheduler(Executors.newScheduledThreadPool(3));
            那么仍然可能导致最后一个任务被阻塞不能定时执行
            **/
        }
    }

     添加@EnableScheduling注解到入口类声明上面或配置类上。


    ————————————————
    版权声明:本文为CSDN博主「一个行走的民」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/zhaominpro/article/details/84561966

  • 相关阅读:
    阿里面试后的问题总结
    Spring IOC源码实现流程
    Spring Aop源码分析
    SpringCloud的分布式配置及消息总线
    阿里java编码规范考试总结
    压缩文件的压缩时候中文乱码码
    mybatis的时间比较 xml 及不解析<=的写法
    批量插入一张表的数据,并且生成不同的uuid 字符截取 批量更新 去除重复数据
    Redis集群的搭建
    Python 之 基础知识(二)
  • 原文地址:https://www.cnblogs.com/muxi0407/p/11936221.html
Copyright © 2011-2022 走看看