文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Spring多定时任务@Scheduled执行阻塞问题解决

2024-04-02 19:55

关注

一. 问题描述

最近项目中发现一个问题,计划每日凌晨4:40执行一个定时任务,使用注解方式: @Scheduled(cron = “0 40 4 * * ?”),cron表达式明显没有问题,但是这个定时任务总是不按时执行,有时候得等到8点多,有时候9点多才执行。后来查了下,原来这种定时方式默认是单线程执行的,恰好我这里有多个定时任务,并且其中有个在4:40之前的定时任务比较耗时,导致4:40的任务只能等待之前的任务执行完成才能够触发,所以要自己手动把定时任务设置成多线程的方式才行。

二. 场景复现

项目描述:使用Springboot进行开发
设置两个定时任务,每5s执行一次,并打印出其执行情况
代码如下:

@Component
@Log4j2
public class ScheduledTask {
    @Scheduled(cron = "0/5 * * * * ?")
    public void task1() throws InterruptedException {
        log.info("I am task11111111, current thread: {}", Thread.currentThread());
        while (true) {
            //模拟耗时任务,阻塞10s
            Thread.sleep(10000);
            break;
        }
    }

    @Scheduled(cron = "0/5 * * * * ?")
    public void task2() {
        log.info("I am task22222222, current thread: {}", Thread.currentThread());
    }
}

执行结果如下:

2019-04-24 17:11:15.008  INFO 16868 --- [   scheduling-1] com.example.demo.task.ScheduledTask      : I am task22222222, current thread: Thread[scheduling-1,5,main]
2019-04-24 17:11:15.009  INFO 16868 --- [   scheduling-1] com.example.demo.task.ScheduledTask      : I am task11111111, current thread: Thread[scheduling-1,5,main]
2019-04-24 17:11:25.009  INFO 16868 --- [   scheduling-1] com.example.demo.task.ScheduledTask      : I am task22222222, current thread: Thread[scheduling-1,5,main]
2019-04-24 17:11:30.002  INFO 16868 --- [   scheduling-1] com.example.demo.task.ScheduledTask      : I am task22222222, current thread: Thread[scheduling-1,5,main]
2019-04-24 17:11:30.003  INFO 16868 --- [   scheduling-1] com.example.demo.task.ScheduledTask      : I am task11111111, current thread: Thread[scheduling-1,5,main]
2019-04-24 17:11:40.004  INFO 16868 --- [   scheduling-1] com.example.demo.task.ScheduledTask      : I am task22222222, current thread: Thread[scheduling-1,5,main]

由结果可见,task1与task2由同一个线程Thread[scheduling-1,5,main]执行,也即该定时任务默认使用单线程,并且由于task1阻塞了10s,导致本应5s执行一次的定时任务10s才执行一次。

三. 解决方案

网上有多种解决方案,以下列举两种

方案一:使用@Async注解实现异步任务

这种方式比较简单,在定时任务上加上@Async注解,注意:需启动类配合加上 @EnableAsync才会生效

代码如下:

@Component
@Log4j2
public class ScheduledTask {
    @Async
    @Scheduled(cron = "0/5 * * * * ?")
    public void task1() throws InterruptedException {
        log.info("I am task11111111, current thread: {}", Thread.currentThread());
        while (true) {
            //模拟耗时任务,阻塞10s
            Thread.sleep(10000);
            break;
        }
    }

    @Async
    @Scheduled(cron = "0/5 * * * * ?")
    public void task2() {
        log.info("I am task22222222, current thread: {}", Thread.currentThread());
    }
}

运行结果:

2019-04-24 17:03:00.024  INFO 2152 --- [         task-1] com.example.demo.task.ScheduledTask      : I am task22222222, current thread: Thread[task-1,5,main]
2019-04-24 17:03:00.024  INFO 2152 --- [         task-2] com.example.demo.task.ScheduledTask      : I am task11111111, current thread: Thread[task-2,5,main]
2019-04-24 17:03:05.001  INFO 2152 --- [         task-3] com.example.demo.task.ScheduledTask      : I am task11111111, current thread: Thread[task-3,5,main]
2019-04-24 17:03:05.001  INFO 2152 --- [         task-4] com.example.demo.task.ScheduledTask      : I am task22222222, current thread: Thread[task-4,5,main]
2019-04-24 17:03:10.002  INFO 2152 --- [         task-5] com.example.demo.task.ScheduledTask      : I am task22222222, current thread: Thread[task-5,5,main]
2019-04-24 17:03:10.003  INFO 2152 --- [         task-6] com.example.demo.task.ScheduledTask      : I am task11111111, current thread: Thread[task-6,5,main]

由运行日志可见,定时每5s执行一次已生效,且每次任务使用的线程不一样,也即实现了多线程执行定时任务,不会出现任务等待现象。此方式据说默认线程池大小为100,要是任务不多的话有点大材小用了,所以我觉得第二种方式比较好。

方案二:手动设置定时任务的线程池大小

定时任务代码部分还原,不使用@Async注解,新增启动代码配置:

@Configuration
public class AppConfig implements SchedulingConfigurer {
    @Bean
    public Executor taskExecutor() {
        //指定定时任务线程数量,可根据需求自行调节
        return Executors.newScheduledThreadPool(3);
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        scheduledTaskRegistrar.setScheduler(taskExecutor());
    }
}

运行结果如下:

2019-04-24 17:26:15.008  INFO 2164 --- [pool-1-thread-2] com.example.demo.task.ScheduledTask      : I am task22222222, current thread: Thread[pool-1-thread-2,5,main]
2019-04-24 17:26:15.008  INFO 2164 --- [pool-1-thread-1] com.example.demo.task.ScheduledTask      : I am task11111111, current thread: Thread[pool-1-thread-1,5,main]
2019-04-24 17:26:20.002  INFO 2164 --- [pool-1-thread-2] com.example.demo.task.ScheduledTask      : I am task22222222, current thread: Thread[pool-1-thread-2,5,main]
2019-04-24 17:26:25.001  INFO 2164 --- [pool-1-thread-2] com.example.demo.task.ScheduledTask      : I am task22222222, current thread: Thread[pool-1-thread-2,5,main]
2019-04-24 17:26:30.001  INFO 2164 --- [pool-1-thread-1] com.example.demo.task.ScheduledTask      : I am task11111111, current thread: Thread[pool-1-thread-1,5,main]
2019-04-24 17:26:30.001  INFO 2164 --- [pool-1-thread-3] com.example.demo.task.ScheduledTask      : I am task22222222, current thread: Thread[pool-1-thread-3,5,main]
2019-04-24 17:26:35.001  INFO 2164 --- [pool-1-thread-3] com.example.demo.task.ScheduledTask      : I am task22222222, current thread: Thread[pool-1-thread-3,5,main]

由结果可见,第二种方式也实现了多线程任务调度。

四. 总结

 两种方式各有优缺点:

比较方案一方案二
优点注解方式使用简单,代码量少配置灵活,线程数可控
缺点线程数不可控,可能存在资源浪费需要增加编码

留个坑,从日志上看@Async方式针对同一任务也是异步的,也即task1每5s会执行一次,但是方式二貌似对同一个任务不会生效,task1执行的时候需等待上一次执行结束才会触发,并没有每5s执行一次。关于这个现象,下次再琢磨…

参考链接:https://segmentfault.com/a/1190000015267976

到此这篇关于Spring多定时任务@Scheduled执行阻塞问题解决的文章就介绍到这了,更多相关Spring多定时任务阻塞内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯