在日常项目运行中,我们总会有需求在某一时间段周期性的执行某个动作。比如每天在某个时间段导出报表,或者每隔多久统计一次现在在线的用户量。在springboot中可以有很多方案去帮我们完成定时器的工作,有Java自带的java.util.Timer类,也有强大的调度器Quartz,还有SpringBoot自带的Scheduled,今天主要说说Scheduled。

一、定时器比较

框架名称 Cron表达式 固定间隔执行 固定频率执行 任务持久化 难易度

TimerTask

不支持 支持 支持 不支持 一般

schedule

支持 支持 支持 不支持 简单

Quartz

支持 支持 支持 支持

在实际应用中,如果没有分布式场景(quartz 支持分布式, schedule 不支持(需要自己实现,用分布式锁,哪个拿到了这把锁,哪个就行执行),schedule跟spring结合的更好,还是很适用的。

二、SpringBoot的定时任务

使用SpringBoot创建定时任务非常简单,目前主要有以下三种创建方式:

  • 基于注解
  • 基于SchedulingConfigurer接口,我们可以从数据库中读取指定时间来动态执行定时任务
  • 基于注解设定多线程定时任务

1. 基于注解@Scheduled

注解@Scheduled默认为单线程,开启多个任务时,任务的执行时机会受上一个任务执行时间的影响。即多个任务都是在同个线程中先后执行。

  1. @Configuration //标识为配置类
  2. @EnableScheduling // 开启定时任务
  3. public class ScheduleTask {
  4. //3.添加定时任务
  5. @Scheduled(cron = "0/5 * * * * ?")
  6. //或直接指定时间间隔,例如:5秒
  7. //@Scheduled(fixedRate=5000)
  8. private void configureTasks() {
  9. System.err.println("执行静态定时任务时间: " + LocalDateTime.now());
  10. }
  11. }

@Scheduled除了支持灵活的参数表达式cron之外,还支持简单的延时操作,例如 fixedDelay ,fixedRate 填写相应的毫秒数即可。

cron表达式详解:https://www.cnblogs.com/myitnews/p/11863041.html

2. 基于SchedulingConfigurer接口

@Schedule注解的一个缺点就是其定时时间不能动态更改,它适用于具有固定任务周期的任务,若要修改任务执行周期,只能走“停服务→修改任务执行周期→重启服务”这条路。而基于 SchedulingConfigurer 接口方式可以做到。SchedulingConfigurer 接口可以实现在@Configuration 类上,同时不要忘了,还需要@EnableScheduling 注解的支持。

接口源码:

  1. @FunctionalInterface
  2. public interface SchedulingConfigurer {
  3. void configureTasks(ScheduledTaskRegistrar var1);
  4. }

ScheduledTaskRegistrar类包括以下几个重要方法:

  • void addTriggerTask(Runnable task, Trigger trigger)
  • void addTriggerTask(TriggerTask task)
  • void addCronTask(Runnable task, String expression)
  • void addCronTask(CronTask task)
  • void addFixedRateTask(Runnable task, long interval)
  • void addFixedRateTask(IntervalTask task)
  • void addFixedDelayTask(Runnable task, long delay)
  • void addFixedDelayTask(IntervalTask task)
  1. @Component
  2. @EnableScheduling
  3. public class TestTask implements SchedulingConfigurer {
  4.  
  5. @Override
  6. public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
  7. taskRegistrar.addTriggerTask(new Runnable() {
  8. @Override
  9. public void run() {
  10. // 定时任务要执行的内容
  11. System.out.println("【开始执行定时任务。。。】");
  12. }
  13. }, new Trigger() {
  14. @Override
  15. public Date nextExecutionTime(TriggerContext triggerContext) {
  16. // 定时任务触发,可修改定时任务的执行周期
  17. String cron = "0 0/5 * * * ?"; //可以将表达式配置在数据库中
  18. CronTrigger trigger = new CronTrigger(cron);
  19. Date nextExecDate = trigger.nextExecutionTime(triggerContext);
  20. return nextExecDate;
  21. }
  22. });
  23. }
  24. }

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

如果确实可能存在数据库表达式错误的,可以是使用org.springframework.scheduling.support.CronSequenceGenerator的isValidExpression验证下,错误的给个默认执行表达式。

3. 多线程定时任务

多线程定时任务是基于@Async注解的。

  1. @Component
  2. @EnableScheduling // 1.开启定时任务
  3. @EnableAsync // 2.开启多线程
  4. public class MultithreadScheduleTask {
  5.  
  6. @Async
  7. @Scheduled(fixedDelay = 1000) //间隔1秒
  8. public void first() throws InterruptedException {
  9. System.out.println("第一个定时任务开始 : " + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName());
  10. System.out.println();
  11. Thread.sleep(1000 * 10);
  12. }
  13.  
  14. @Async
  15. @Scheduled(fixedDelay = 2000)
  16. public void second() {
  17. System.out.println("第二个定时任务开始 : " + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName());
  18. System.out.println();
  19. }
  20. }

第一个定时任务和第二个定时任务互不影响;由于开启了多线程,第一个任务的执行时间也不受其本身执行时间的限制,所以需要注意可能会出现重复操作导致数据异常。

三、分布式环境下的定时任务

我们有很多时候都需要一些定时任务的辅助,大多数情况,定时任务都可能是写到一个服务节点。但是可能存在以下情况:

  • 如果业务逻辑过于复杂的话,不好维护。
  • 如果服务节点挂了,那么所有的定时任务都不会执行了。

可能有人说部署多个节点不久可以了吗?确实,但是有没有想过,定时任务会重复执行,这怎么解决呢?使用redis分布式锁。

查看本教程中的 “RedisTemplate详解” ,获取RedisUtil的代码。

  1. boolean flag = false;
  2. try {
  3. //1. 获取锁
  4. flag = RedisUtil.lock("smsSend", 1000); //key和过期时间,过期时间根据实际业务预测
  5. if(flag) {
  6. //2. 执行业务逻辑
  7. }else {
  8. logger.info("分布式锁没有获取到锁:{}");
  9. }
  10. } catch (Exception e) {
  11. logger.info("定时任务执行失败,分布式锁异常"+e);
  12. }finally {
  13. if(flag) {//释放锁
  14. RedisUtil.unlock("smsSend");
  15. logger.info("锁已释放");
  16. }
  17. }

【使用篇二】SpringBoot定时任务Scheduled(14)的更多相关文章

  1. SpringBoot学习笔记(七):SpringBoot使用AOP统一处理请求日志、SpringBoot定时任务@Scheduled、SpringBoot异步调用Async、自定义参数

    SpringBoot使用AOP统一处理请求日志 这里就提到了我们Spring当中的AOP,也就是面向切面编程,今天我们使用AOP去对我们的所有请求进行一个统一处理.首先在pom.xml中引入我们需要的 ...

  2. SpringBoot定时任务@Scheduled

    SpringBoot定时任务主要由两个注解完成. @Scheduled加在方法上面. @EnableScheduling加在类上面.可以是Application类,也可以是@Component类,还可 ...

  3. Springboot定时任务@Scheduled注解形式,参数详解

    参数详解 1.占位符 1 秒 是 0-59 , - * / 2 分 是 0-59 , - * / 3 时 是 0-23 , - * / 4 日 是 1-31 , - * ? / L W 5 月 是 1 ...

  4. spring boot 学习(八)定时任务 @Scheduled

    SpringBoot 定时任务 @Scheduled 前言 有时候,我们有这样的需求,需要在每天的某个固定时间或者每隔一段时间让应用去执行某一个任务.一般情况下,可以使用多线程来实现这个功能:在 Sp ...

  5. SpringBoot学习18:springboot使用Scheduled 定时任务器

    Scheduled 定时任务器:是 Spring3.0 以后自带的一个定时任务器. 1.在pom.xml文件中添加Scheduled依赖 <!-- 添加spring定时任务 Scheduled ...

  6. SpringBoot 定时任务篇

    一. 基于注解@Scheduled默认为单线程,开启多个任务时,任务的执行时机会受上一个任务执行时间的影响. 1.创建定时器 使用SpringBoot基于注解来创建定时任务非常简单,只需几行代码便可完 ...

  7. springboot 基于@Scheduled注解 实现定时任务

    前言 使用SpringBoot创建定时任务非常简单,目前主要有以下三种创建方式: 一.基于注解(@Scheduled) 二.基于接口(SchedulingConfigurer) 前者相信大家都很熟悉, ...

  8. 14 微服务电商【黑马乐优商城】:day02-springcloud(理论篇二:知道什么是SpringCloud)

    本项目的笔记和资料的Download,请点击这一句话自行获取. day01-springboot(理论篇) :day01-springboot(实践篇) day02-springcloud(理论篇一: ...

  9. SpringBoot执行定时任务@Scheduled

    SpringBoot执行定时任务@Scheduled 在做项目时,需要一个定时任务来接收数据存入数据库,后端再写一个接口来提供该该数据的最新的那一条. 数据保持最新:设计字段sign的值(0,1)来设 ...

随机推荐

  1. 【重温基础】15.JS对象介绍

    从这篇文章开始,复习 MDN 中级教程 的内容了,在初级教程中,我和大家分享了一些比较简单基础的知识点,并放在我的 [Cute-JavaScript]系列文章中. 关于[Cute-JavaScript ...

  2. 【CSS】305- [译] Web 使用 CSS Shapes 的艺术设计

    %; %; %; %; 0, 0 100%, 100% 100%); %; %; % 0, 0 100%, 100% 100%); %; %; ) p:nth-of-type(1)::before { ...

  3. v-if和v-show 的区别

    区别 1.手段:v-if是通过控制dom节点的存在与否来控制元素的显隐:v-show是通过设置DOM元素的display样式,block为显示,none为隐藏: 2.编译过程:v-if切换有一个局部编 ...

  4. Java垃圾回收机制你还不明白?一线大厂面试必问的!

    什么是自动垃圾回收? 自动垃圾回收是一种在堆内存中找出哪些对象在被使用,还有哪些对象没被使用,并且将后者删掉的机制. 所谓使用中的对象(已引用对象),指的是程序中有指针指向的对象:而未使用中的对象(未 ...

  5. 安装mysql5.7(亲手实操)

    https://files-cdn.cnblogs.com/files/yucoder/%E5%AE%89%E8%A3%85mysql5.7.pdf

  6. 如何将hive表中的数据导出

    近期经常将现场的数据带回公司测试,所以写下该文章,梳理一下思路. 1.首先要查询相应的hive表,比如我要将c_cons这张表导出,我先查出hive中是否有这张表. 查出数据,证明该表在hive中存在 ...

  7. 字符串的扩展(ES6)

    文章目录 字符串的扩展 1. 字符的Unicode表示法 2. codePointAt() 3. String.fromCodePoint() 4. 字符串的遍历器接口 5. at()(提案) 6. ...

  8. 【nginx+keepalived】nginx+keepalived搭建高可用

    一.结构及环境 1.1 环境介绍 操作系统:centos7 nginx+keepalived:106.53.73.200 master nginx+keepalived:182.254.184.102 ...

  9. 高版本Visual Studio和低版本ArcGIS共存 工具箱没有控件的解决方法

    转载请声明.博客园/B站/CSDN/知乎/小专栏 @秋意正寒 欢迎访问小专栏,更多WebGIS开发(Cesium等)经验分享:https://xiaozhuanlan.com/gishome 众所周知 ...

  10. WebAPI + log4net日志 存入数据库

    1.首先选择你的项目 打开net管理控制台 输入 install-package log4net 进行安装  也可以 在net包 搜索 log4net 2.安装完之后 在Models文件夹 创建一个L ...