前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring定时任务的一个坑

Spring定时任务的一个坑

作者头像
十毛
发布2019-03-27 14:58:14
1K0
发布2019-03-27 14:58:14
举报

最近遇到一个Spring定时任务没有及时启动,经常过30分钟以上才执行。最后定位原因,是因为所有的Spring定时任务共用同一个线程,如果有一个线程耗时太长,会导致后续定时任务阻塞。

好奇怪

排查问题,发现定时器设置正确(每分钟执行一次),而且在任务的第一行就输出日志。

代码语言:javascript
复制
@Scheduled(fixedRate = 60 * 1000)
代码语言:javascript
复制
[2019-01-10 18:13:56.913][pool-6-thread-1][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 18:13:56.938][pool-6-thread-1][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 18:38:49.103][pool-6-thread-1][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 18:38:49.119][pool-6-thread-1][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 19:02:40.755][pool-6-thread-1][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 19:02:40.770][pool-6-thread-1][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 19:29:02.527][pool-6-thread-1][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 19:29:02.551][pool-6-thread-1][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 19:56:53.597][pool-6-thread-1][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 19:56:53.615][pool-6-thread-1][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 20:24:05.241][pool-6-thread-1][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 20:24:05.262][pool-6-thread-1][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 20:39:22.504][pool-6-thread-1][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 21:22:02.099][pool-6-thread-1][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 21:22:02.116][pool-6-thread-1][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 21:48:19.265][pool-6-thread-1][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 21:48:19.277][pool-6-thread-1][INFO][Crontab:91][][]: start to run task crontab

日志显示,一般过了20~30分钟,会连续执行两次任务。 看起来是Spring定时任务出错了,感觉有点崩溃,不能相信Spring会出现这么简单的错误。但是事实又这么明显

再思考

  • 从日志来看,执行定时任务的线程名是 pool-6-thread-1,属于某一个线程池。那么可能是线程池的线程数不够,那么找到这个线程之前在做的事情就知道为什么这个线程一直没来执行定时任务

执行命令:

代码语言:javascript
复制
grep pool-6-thread-1 all.log

果然找到问题,原来pool-6-thread-1线程在执行另外一个耗时很长的任务

问题定位

Spring定时任务默认使用的SingleThreadScheduledExecutor,所有任务共用一个线程,所以会互相等待。

代码语言:javascript
复制
protected void scheduleTasks() {
    if (this.taskScheduler == null) {
        //使用的是SingleThreadScheduledExecutor(只有一个线程)
        this.localExecutor = Executors.newSingleThreadScheduledExecutor();
        this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
    }
    if (this.triggerTasks != null) {
        for (TriggerTask task : this.triggerTasks) {
            addScheduledTask(scheduleTriggerTask(task));
        }
    }
    if (this.cronTasks != null) {
        for (CronTask task : this.cronTasks) {
            addScheduledTask(scheduleCronTask(task));
        }
    }
    if (this.fixedRateTasks != null) {
        for (IntervalTask task : this.fixedRateTasks) {
            addScheduledTask(scheduleFixedRateTask(task));
        }
    }
    if (this.fixedDelayTasks != null) {
        for (IntervalTask task : this.fixedDelayTasks) {
            addScheduledTask(scheduleFixedDelayTask(task));
        }
    }
}

解决办法

最简单的办法就是减少定时任务耗时,但是本文还介绍另外一种 自定义定时任务线程池,实现方式如下:

  • 增加两个注解:@EnableAsync@Async
代码语言:javascript
复制
@EnableScheduling
@EnableAsync
public class Crontab {

    @Scheduled(fixedRate = 60)
    @Async
    public void doTask() {
        log.info("start to run task crontab");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  • 配置线程池
代码语言:javascript
复制
<task:annotation-driven scheduler="crontab-scheduler"/>
<task:scheduler id="crontab-scheduler" pool-size="10"/>
  • 运行结果
代码语言:javascript
复制
[2019-01-10 22:31:24.120][crontab-scheduler-3][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 22:31:24.180][crontab-scheduler-2][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 22:31:24.240][crontab-scheduler-4][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 22:31:24.300][crontab-scheduler-7][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 22:31:24.360][crontab-scheduler-6][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 22:31:24.420][crontab-scheduler-5][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 22:31:24.480][crontab-scheduler-9][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 22:31:24.540][crontab-scheduler-10][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 22:31:24.600][crontab-scheduler-8][INFO][Crontab:91][][]: start to run task crontab
[2019-01-10 22:31:34.100][crontab-scheduler-1][INFO][Crontab:91][][]: start to run task crontab

日志可以看出来,定时任务使用了10个线程

最佳实践

  • 定时任务不能执行耗时任务,阻塞其他任务的执行
  • 可以使用自定义线程池

参考

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.01.10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 好奇怪
  • 再思考
  • 问题定位
  • 解决办法
  • 最佳实践
  • 参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档