当前位置:   article > 正文

SpringBoot 之定时任务详解_@scheduled(fixedrate = 1000)

@scheduled(fixedrate = 1000)

SpringBoot整合定时任务

/**
 * 定时任务
 *  1、@EnableScheduling 开启定时任务
 *  2、@Scheduled 开启一个定时任务
 *  3、自动配置类 TaskSchedulingAutoConfiguration
 * 异步任务
 *  1、@EnableAsync 开启异步任务功能
 *  2、@Async 方法异步执行
 *  3、自动配置类 TaskExecutionAutoConfiguration
 */
 
/**
 * 在Spring中 只允许6位 [* * * ? * 1] : 每周一每秒执行一次
 * 					   [* /5 * * ? * 1] : 每周一 每5秒执行一次
 *
    * 	定时任务默认是阻塞的,不应该阻塞。
    * 	解决阻塞方案:
    * 	    1、可以让业务用异步方式运行,自己提交到线程池
    * 	    2、让定时任务异步执行 @EnableAsync + @Async
    *
 */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

静态:基于注解

@Scheduled 定时任务的核心。
@Scheduled默认为单线程,开启多个任务时,任务的执行时机会受上一个任务执行时间的影响。
参数解析:

  • cron: cron表达式,根据表达式循环执行,与fixedRate属性不同的是它是将时间进行了切割。(@Scheduled(cron = "0/5 * * * * *")任务将在5、10、15、20...这种情况下进行工作)
  • fixedRate: 每隔多久执行一次;(@Scheduled(fixedRate = 1000) 假设第一次工作时间为2018-05-29 16:58:30,工作时长为3秒,那么下次任务的时候就是2018-05-29 16:58:33,配置成异步后,只要到了执行时间就会开辟新的线程工作),如果(@Scheduled(fixedRate = 3000) 假设第一次工作时间为2018-05-29 16:58:30,工作时长为1秒,那么下次任务的时间依然是2018-05-29 16:58:33
  • fixedDelay: 当前任务执行完毕后等待多久继续下次任务(@Scheduled(fixedDelay = 3000) 假设第一次任务工作时间为2018-05-29 16:54:33,工作时长为5秒,那么下次任务的时间就是2018-05-29 16:54:41
  • initialDelay: 第一次执行延迟时间,只是做延迟的设定,与fixedDelay关系密切,配合使用,相辅相成。

@Async 代表该任务可以进行异步工作,由原本的串行改为并行。

单线程案例:

@Slf4j
@Component
@EnableScheduling	// 开启定时任务
public class SpringTask {

    @Scheduled(cron = "0/1 * * * * *")
    public void scheduled2() throws InterruptedException {
        Thread.sleep(3000);
        log.info("scheduled2 每1秒执行一次:{}", LocalDateTime.now());
    }

    @Scheduled(fixedRate = 1000)
    public void scheduled1() throws InterruptedException {
        Thread.sleep(3000);
        log.info("scheduled1 每1秒执行一次:{}", LocalDateTime.now());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在这里插入图片描述
发现两个任务都是同一个线程,任务之间会相互影响。

多线程案例:

@Slf4j
@Component
@EnableScheduling	// 开启定时任务
@EnableAsync		// 开启多线程
public class SpringTask {

    @Async
    @Scheduled(fixedDelay = 1000)  //间隔1秒
    public void first() throws InterruptedException {
        System.out.println("第一个定时任务开始 : " + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName());
        System.out.println();
        Thread.sleep(1000 * 10);
    }

    @Async
    @Scheduled(fixedDelay = 2000)
    public void second() {
        System.out.println("第二个定时任务开始 : " + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName());
        System.out.println();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

在这里插入图片描述
从控制台可以看出,第一个定时任务和第二个定时任务互不影响;
并且,由于开启了多线程,第一个任务的执行时间也不受其本身执行时间的限制,所以需要注意可能会出现重复操作导致数据异常。

Cron表达式参数分别表示:

  • 秒(0~59) 例如0/5表示每5秒
  • 分(0~59)
  • 时(0~23)
  • 日(0~31)的某天,需计算
  • 月(0~11)
  • 周几( 可填1-7 或 SUN/MON/TUE/WED/THU/FRI/SAT)

cron表达式在线生成: http://cron.qqe2.com/

显然,使用@Scheduled 注解很方便,但缺点是当我们调整了执行周期的时候,需要重启应用才能生效,这多少有些不方便。为了达到实时生效的效果,可以使用接口来完成定时任务。

动态:基于接口

数据库脚本。

CREATE TABLE `cron`  (
  `cron_id` varchar(30) NOT NULL PRIMARY KEY,
  `cron` varchar(30) NOT NULL  
);
INSERT INTO `cron` VALUES ('1', '0/5 * * * * ?');
  • 1
  • 2
  • 3
  • 4
  • 5

创建定时器。
这里添加的是TriggerTask,目的是循环读取数据库设置好的执行周期,以及执行相关定时任务的内容。
具体代码如下:

@Component
@EnableScheduling   // 开启定时任务
public class DynamicScheduleTask implements SchedulingConfigurer {
    @Resource
    CronService cronService;
    
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        // 定时任务要执行的内容
        Runnable task = ()->{
            System.out.println("执行动态定时任务: " + LocalDateTime.now().toLocalTime());
        };
        // 设置执行周期
        Trigger trigger = triggerContext -> {
            List<Cron> crons = cronService.list();
            String cron = crons.get(0).getCron();
            // 合法性校验.
            if (StringUtils.isEmpty(cron)) {
                // Omitted Code ..
            }
            // 返回执行周期(Date)
            return new CronTrigger(cron).nextExecutionTime(triggerContext);
        };
        // 注册一个任务
        taskRegistrar.addTriggerTask(task, trigger);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

测试。
在这里插入图片描述
打印时间是每5秒一次,
将执行周期修改为每3秒执行一次,
发现执行周期已经改变,并且不需要重启应用。

注意: 如果在数据库修改时格式出现错误,则定时任务会停止,即使重新修改正确;此时只能重新启动项目才能恢复。

@Scheduled定时任务每次执行两次解决方案

一般出现在SpringMVC的项目中,因为有两个上下文,一个是SpringMVC容器,一个Spring容器,
如果你的定时任务类同时被两个容器管理了,就会执行两次。

解决方案是只被一个容器来管理。

定时任务cron表达式详解

cron表达式实际上是由七个子表达式组成。这些表达式之间用空格分隔:
1、Seconds (秒)
2、Minutes(分)
3、Hours(小时)
4、Day-of-Month (天)
5、Month(月)
6、Day-of-Week (周)
7、Year(年)
例: 0 0 12 ? * WED 意思是:每个星期三的中午12点执行。

子表达式范围:
1、Seconds (0~59)
2、Minutes (0~59)
3、Hours (0~23)
4、Day-of-Month (1~31,但是要注意有些月份没有31天)
5、Month (0~11,或者"JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV,DEC")
6、Day-of-Week (1~7,1=SUN 或者"SUN, MON, TUE, WED, THU, FRI, SAT”)
7、Year (1970~2099)

表达式例子:
0 * * * * ? 每1分钟触发一次
0 0 * * * ? 每天每1小时触发一次
0 0 10 * * ? 每天10点触发一次
0 * 14 * * ? 在每天下午2点到下午2:59期间的每1分钟触发
0 30 9 1 * ? 每月1号上午9点半
0 15 10 15 * ? 每月15日上午10:15触发

*/5 * * * * ? 每隔5秒执行一次
0 */1 * * * ? 每隔1分钟执行一次
0 0 5-15 * * ? 每天5-15点整点触发
0 0/3 * * * ? 每三分钟触发一次
0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发
0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发
0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
0 0 10,14,16 * * ? 每天上午10点,下午2点,4点

0 0 12 ? * WED 表示每个星期三中午12点
0 0 17 ? * TUES,THUR,SAT 每周二、四、六下午五点
0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发
0 15 10 ? * MON-FRI 周一至周五的上午10:15触发

0 0 23 L * ? 每月最后一天23点执行一次
0 15 10 L * ? 每月最后一日的上午10:15触发
0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发

0 15 10 * * ? 2005 2005年的每天上午10:15触发
0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最后一个星期五上午10:15触发
0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发

参考:
springboot2.3.7集成定时任务SchedulingConfigurer
spring - @schedule定时任务器

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/blog/article/detail/52513
推荐阅读
相关标签
  

闽ICP备14008679号