本文介绍: OK,果然如此,注意这个是任务结束后每隔五秒,如果方法中间加了一个sleep方法,那么执行时间还要加上sleep里面的值,比如说中间加了一个sleep(1000),那么就会每隔6秒执行一次比如我们设置两个定时任务,那么因为Spring Task是单线程,如果在第一个定时任务加了一个sleep方法,那么会等第一个方法响应后在执行第二个任务,就很浪费cpu运行时间。该域可以省略,表示每年都触发执行效果如上,确实不会影响到任务2的运行,但是如果定时任务过多,超过了配置线程池的线程数量还是会运行错乱。

目录

一、定时任务的理解

二、入门案例

三、Cron表达式

四、Cron实战案例

五、多线程案例


一、定时任务的理解

定时任务即系统在特定时间执行一段代码,它的场景应用非常广泛:

定时任务的实现主要有以下几种方式

  1. Java自带java.util.Timer类,这个类允许调度一个java.util.TimerTask任务。使用这种方式可以程序按照某一个频度执行,但不能在指定时间运行。一般用的较少。
  2. Quartz。这是一个功能比较强大的的调度器,可以程序指定时间执行,也可以按照某一个频度执行配置起来稍显复杂
  3. Spring3.0以后自带Spring Task,可以将它看成一个轻量级的Quartz,使用起来比 Quartz简单许多,在课程我们使用Spring Task实现定时任务

二、入门案例

创建SpringBoot项目,在启动开启定时任务。

也就是在启动类上方添加@EnableScheduling注解即可开启定时任务,代码如下

package com.example.springboottaskdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class SpringboottaskdemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringboottaskdemoApplication.class, args);
    }

}

编写定时任务类 

@Component
public class MyTask {
  // 定时任务方法,每秒执行一次
  @Scheduled(cron="* * * * * *")
  public void task1() {
    SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    System.out.println(sdf.format(new Date()));
 }
}

启动项目,定时任务方法按照配置定时执行。 

OK,果然如此,每隔一秒输出当前时间

@Scheduled写在方法上方,指定方法定时执行。常用参数如下

  1. croncron表达式定义方法执行的时间规则
  2. fixedDelay:任务立即执行,之后每隔多久执行一次单位是毫秒,上一次任务结束计算下次执行的时间。

OK,先来一个案例代码如下:任务结束后每隔五秒执行一次

// 立即执行,任务结束后五秒执行一次
    @Scheduled(fixedDelay = 5000)
    public void task1() throws InterruptedException {
        SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
        System.out.println("Task1: "+sdf.format(new Date()));
    }

效果如下: 

OK,果然如此,注意这个是任务结束后每隔五秒,如果方法中间加了一个sleep方法,那么执行时间还要加上sleep里面的值,比如说中间加了一个sleep(1000),那么就会每隔6秒执行一次

fixedRate:任务立即执行,之后每隔多久执行一次单位是毫秒,上一次任务开始后计算下次执行的时间。

案例如下代码如下

// 立即执行,之后每五秒执行一次
    @Scheduled(fixedRate = 5000)
    public void task2() throws InterruptedException {
        SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
        // 没有影响五秒输出一次
        Thread.sleep(1000);
        System.out.println("Task2: "+sdf.format(new Date()));
    }

 OK,看如下执行代码确实是不受到sleep影响

initialDelay:项目启动后不马上执行定时器,根据initialDelay的值延时执行。 为了突出刚刚说的fixedDelay会受到sleep影响这里配合fixedDelay来结合测试演示一下:

代码如下:

// 项目启动后三秒执行,之后每六秒执行一次
    @Scheduled(fixedDelay = 5000,initialDelay = 3000)
    public void task3() throws InterruptedException {
        SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
        // 没有影响五秒输出一次
        Thread.sleep(1000);
        System.out.println("Task3: "+sdf.format(new Date()));
    }

OK,看运行结果也是隔了三秒才出现第一次打印时间,并且打印时间是隔六秒打印一次 

三、Cron表达式

Spring Task依靠Cron表达式配置定时规则。Cron表达式是一个字符串,分为6或7个域,每一个域代表一个含义,以空格隔开。有如下两种语法格式

  1. Seconds Minutes Hours DayofMonth Month DayofWeek Year
  2. Seconds Minutes Hours DayofMonth Month DayofWeek

Seconds(秒):域中可出现 , – * / 四个字符,以及0-59的整数

Minutes(分):域中可出现 , – * / 四个字符,以及0-59的整数
Hours(时):域中可出现 , – * / 四个字符,以及0-23的整数
DayofMonth日期):域中可出现 , – * / ? L W C 八个字符,以及1-31的整数

  • C :表示和当前日期相关联。在DayofMonth域使用 5C ,表示在5日后的那一天触发,且每月的那天都会触发。比如当前是10号,那么每月15号都会触发。
  • L :表示最后,在DayofMonth域使用 L ,表示每个月的最后一天触发。
  • W :表示工作日,在DayofMonth域用 15W ,表示最接近这个月第15天的工作日触发,如果15号是周六,则在14号即周五触发;如果15号是周日,则在16号即周一触发;如果15号是周二则在当天触发。

注:

  1. 用法只会在当前月计算,不会到下月触发。比如在DayofMonth域用 31W ,31号是周日,那么会在29号触发而不是下月1号。
  2. 在DayofMonth域用 LW ,表示这个月的最后一个工作日触发。

Month(月份):域中可出现 , – * / 四个字符,以及1-12的整数或JAN-DEC的单词缩写
DayofWeek(星期):可出现 , – * / ? L # C 八个字符,以及1-7的整数或SUN-SAT 单词缩写,1代表星期天,7代表星期六

  • C :在DayofWeek域使用 2C ,表示在2日后的那一天触发,且每周的那天都会触发。比如当前是周一,那么每周三都会触发。
  • L :在DayofWeek域使用 L ,表示在一周的最后一天即星期六触发。在DayofWeek域使用 5L ,表示在一个月的最后一个星期四触发。
  • # :用来指定具体的周数, # 前面代表星期几, # 后面代表一个月的第几周,比如 5#3 表示一个月第三周的星期四。
  • ? :在无法确定是具体哪一天时使用,用于DayofMonth和DayofWeek域。例如在每月的20日零点触发1次,此时无法确定20日是星期几,写法如下: 0 0 0 20 * ? ;或者在每月最后一个周日触发,此时无法确定该日期是几号,写法如下: 0 0 0 ? * 1L

Year(年份):域中可出现 , – * / 四个字符,以及1970~2099的整数。该域可以省略,表示每年都触发。

四、Cron实战案例

下面有常用的案例大家可以参考一下

Cron实战案例
含义 表达式
每隔5分钟触发一次 0 0/5 * * * *
每小时触发一次 0 0 * * * *
每天的7点30分触发 0 30 7 * * *
周一到周五的早上6点30分触发 0 30 6 ? * 2-6
每月最后一天早上10点触发 0 0 10 L * ?
每月最后一个工作日的18点30分触发 0 30 18 LW * ?
2030年8月每个星期六和星期日早上10点触发 0 0 10 ? 8 1,7 2030
每天10点、12点、14点触发 0 0 10,12,14 * * *
朝九晚五工作时间内每半小时触发一次 0 0 0/30 9-17 ? * 2-6
每周三中午12点触发一次 0 0 12 ? * 4
每天12点触发一次 0 0 12 * * *
每天14点到14:59每分钟触发一次 0 * 14 * * *
每天14点到14:59每5分钟触发一次 0 0/5 14 * * *
每天14点到14:05每分钟触发一次 0 0-5 14 * * *
每月15日上午10:15触发 0 15 10 15 * ?
每月最后一天的上午10:15触发 0 15 10 L * ?
每月的第三个星期五上午10:15触发 0 15 10 ? * 6#3

好啦,通过这些大家应该就可以领悟了

五、多线程案例

Spring Task定时器默认是单线程的,如果项目中使用多个定时器,使用一个线程会造成效率低下。

比如我们设置两个定时任务,那么因为Spring Task是单线程,如果在第一个定时任务加了一个sleep方法,那么会等第一个方法响应后在执行第二个任务,就很浪费cpu运行时间。代码如下:

    @Scheduled(cron = "* * * * * *")
    public void task1() throws InterruptedException {
        System.out.println(Thread.currentThread().getId()+"线程执行任务1 - "+new SimpleDateFormat("HH:mm:ss").format(new Date());
        Thread.sleep(5000);
    }

    @Scheduled(cron = "* * * * * *")
    public void task2() throws InterruptedException {
        System.out.println(Thread.currentThread().getId()+"线程执行任务2 - "+new SimpleDateFormat("HH:mm:ss").format(new Date());
    }

执行效果如下:可以看到是先执行了任务2,但是他们都要隔五秒才能运行一次,因为通过线程号可以知道这是同一个线程。

因此任务1较浪费时间,会阻塞任务2的运行。此时我们可以给SpringTask配置线程池。代码如下:

package com.example.springboottaskdemo;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

import java.util.concurrent.Executors;

@Configuration
public class SchedulingConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        // 创建线程池,设置五个线程
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(4));
    }
}

这样就不会出现阻塞问题了,因为两个任务不是同一个线程,接下来我们再次运行看看

执行效果如上,确实不会影响到任务2的运行,但是如果定时任务过多,超过了配置的线程池的线程数量还是会运行错乱。

Ok,SpringBoot到这里就完结撒花了。

原文地址:https://blog.csdn.net/qq_53317005/article/details/133606081

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_19767.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注