文章目录
1. 文章引言
系统中通常有些需要自动执行的任务,这些任务可能每隔一段时间就要执行一次,也可能需要在指定的时间点自动执行,这些任务的自动执行必须使用任务的自动调度。
JDK
为简单的任务调度提供了Timer
支持,如下代码所示:
public static void main(String[] args) {
Timer timer = new Timer();
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
TimerTask timerTask =
new TimerTask() {
@Override
public void run() {
String format = simpleDateFormat.format(new Date());
System.out.println("每隔5秒输出timer信息,输出时间:" + format);
}
};
timer.schedule(timerTask, 0, 5000);
}
输出结果如下图所示:
但对于更复杂的调度,例如:需要在某的时刻调度任务时。Timer
就显得力不从心了,因而出现了Quartz
任务调度框架。
Quartz
是OpenSymphony
开源组织在Job scheduling
领域又一个开源项目,它可以与J2EE
与J2SE
应用程序相结合也可以单独使用。
Quartz
可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs
这样复杂的日程序表——Jobs
可以做成标准的Java
组件或EJBs
。
总之,Quartz
是一款开源且丰富特性的任务调度库,能够集成与任何的java
应用下到独立应用,大到电子商业系统。Quartz
就是基于java
实现的任务调度框架,用于执行你想要执行的任何任务。
什么是任务调度
?任务调度就是我们系统中创建了N
个任务,每个任务都有指定的时间进行执行,而这种多任务的执行策略就是任务调度。
Quartz
的作用就是让任务调度变得更加丰富,高效,安全,而且是基于 Java 实现的,这样子开发者只需要调用几个接口坐下简单的配置,即可实现上述需求。
2. 引入Quartz
Quartz
的使用也非常简单,我们需要在pom.xml
中引入Quartz
的jar
包:
如果你是spring boot
项目,直接引入如下jar
包:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
<version>3.0.4</version>
</dependency>
如果你是spring mvc
项目,则引入如下jar
包:
<!--quartz-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
3. Cron表达式
3.1 简述Cron表达式
Cron表达式
是用来配置CronTrigger
实例,它是一个由 7个子表达式
组成的字符串,每个字符都表示不同的日程细节:
-
Seconds
:秒 -
Minutes
:分钟 -
Hours
:小时 -
Day-of-Month
:月中的哪几天 -
Month
:月 -
Day-of-Week
:周中的哪几天 -
Year
:年
字 段 | 是 否 必 填 | 允 许 值 | 可 用 特 殊 字 符 | 备 注 |
---|---|---|---|---|
秒(Seconds ) |
是 | 0-59 | , - * / | 标准实现不支持此字段。 |
分(Minutes ) |
是 | 0-59 | , - * / | |
小时(Hours ) |
是 | 0-23 | , - * / | |
月中的哪几天(Day of month ) |
是 | 1-31 | , - * / ? L W C | ?LW只有部分软件实现了 |
月(Month ) |
是 | 1-12 或 JAN-DEC | , - * / | |
周中的哪几天(Day of week ) |
是 | 1-7 或 SUN-SAT | , - * / ? L C # | ?L#只有部分软件实现了Linux和Spring的允许值为0-7,07为周日Quartz的允许值为1-7,1为周日 |
年(Year ) |
否 | 不填写 或 1970-2099 | , - * / |
【注意】如下为特殊符号的说明:
特 殊 符 号 | 含 义 |
---|---|
* | 可用在所有字段中,表示对应时间域的每一个时刻,例如,***** 在分钟字段时,表示“每分钟” |
? | 该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于点位符 |
- | 表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12 |
, | 表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五 |
/ | x/y表达一个等步长序列,x为起始值,y为增量步长值。如在分钟字段中使用0/15,则表示为0,15,30和45秒,而5/15在分钟字段中表示5,20,35,50,你也可以使用*/y,它等同于0/y |
L | 该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。L在日期字段中,表示这个月份的最后一天,如一月的31号,非闰年二月的28号;如果L用在星期中,则表示星期六,等同于7。但是,如果L出现在星期字段里,而且在前面有一个数值 X,则表示“这个月的最后X天”,例如,6L表示该月的最后星期五 |
W | 该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。例如15W表示离该月15号最近的工作日,如果该月15号是星期六,则匹配14号星期五;如果15日是星期日,则匹配16号星期一;如果15号是星期二,那结果就是15号星期二。但必须注意关联的匹配日期不能够跨月,如你指定1W,如果1号是星期六,结果匹配的是3号星期一,而非上个月最后的那天。W字符串只能指定单一日期,而不能指定日期范围 |
# | 该字符只能在星期字段中使用,表示当月某个工作日。如6#3表示当月的第三个星期五(6表示星期五,#3表示当前的第三个),而4#5表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发 |
【注意】Cron
表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感。
3.2 Cron常用表达式
常用表达式 | 说明 |
---|---|
0 0 10,14,16 * * ? | 每天上午10点,下午2点,4点 |
0 0/30 9-17 * * ? | 朝九晚五工作时间内每半小时,从0分开始每隔30分钟发送一次 |
0 0 12 ? * WED | 表示每个星期三中午12点 |
0 0 12 * * ? | 每天中午12点触发 |
0 15 10 ? * * | 每天上午10:15触发 |
0 15 10 * * ? | 每天上午10:15触发 |
0 15 10 * * ? * | 每天上午10:15触发 |
0 15 10 * * ? 2005 | 2005年的每天上午10:15触发 |
0 * 14 * * ? | 在每天下午2点到下午2:59期间的每1分钟触发 |
0 0/55 14 * * ? | 在每天下午2点到下午2:55期间,从0开始到55分钟触发 |
0 0-5 14 * * ? | 在每天下午2点到下午2:05期间的每1分钟触发 |
0 10,44 14 ? 3 WED | 每年三月的星期三的下午2:10和2:44触发 |
0 15 10 ? * MON-FRI | 周一至周五的上午10:15触发 添加链接描述 |
0 15 10 15 * ? | 每月15日上午10:15触发 |
0 15 10 L * ? | 每月最后一日的上午10:15触发 |
0 15 10 ? * 6L | 每月的最后一个星期五上午10:15触发 |
0 15 10 ? * 6L 2002-2005 | 2002年至2005年的每月的最后一个星期五上午10:15触发 |
0 15 10 ? * 6#3 | 每月的第三个星期五上午10:15触发 |
3.3 在线生成cron表达式
3.4 在线校验cron表达式
4. Quartz的使用
我们在使用Quartz时,一般是配合spring框架的。我们要在任务调度的方法上,添加@Configuration 、@EnableScheduling和@Scheduled
注解。
- 每隔N秒钟执行一次
以每隔5秒钟执行一次为例子。
/**
* @author super先生
* @datetime 2023/4/8 15:23
* @desc
*/
@Configuration
@EnableScheduling
public class QuartzTest {
/**
* 每隔5秒钟执行一次
*
* @author super先生
* @datetime 2023/4/8 15:59
*/
@Scheduled(cron = "0/5 * * * * ? ")
public void testPerFSecond() {
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = simpleDateFormat.format(new Date());
System.out.println("每隔5秒输出timer信息,输出时间:" + format);
}
}
输出结果如下图:
- 每隔N分钟执行一次
以每隔1分钟执行一次为例子
/**
* @author super先生
* @datetime 2023/4/8 15:23
* @desc
*/
@Configuration
@EnableScheduling
public class QuartzTest {
/**
* 每隔1分钟执行一次
*
* @author super先生
* @datetime 2023/4/8 16:00
*/
@Scheduled(cron = "0 0/1 * * * ? ")
public void testPerOneMinute() {
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = simpleDateFormat.format(new Date());
System.out.println("每隔1分钟输出timer信息,输出时间:" + format);
}
}
输出结果如下图:
- 每隔N小时
以每隔1小时执行一次为例子
/**
* @author super先生
* @datetime 2023/4/8 15:23
* @desc
*/
@Configuration
@EnableScheduling
public class QuartzTest {
/**
* 每隔1小时执行一次
*
* @author super先生
* @datetime 2023/4/8 16:00
*/
@Scheduled(cron = "0 0 0/1 * * ? ")
public void testPerOneHour() {
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = simpleDateFormat.format(new Date());
System.out.println("每隔1小时输出timer信息,输出时间:" + format);
}
}
由于每隔1小时执行一次的时间较长,就没有打印结果,而是将corn表达式,粘贴到在线校验cron表达的网站中,输出结果如下图所示:
- 在N点定时执行一次
以在22点定时执行为例子,也就是每天22点执行一次
/**
* @author super先生
* @datetime 2023/4/8 15:23
* @desc
*/
@Configuration
@EnableScheduling
public class QuartzTest {
/**
* 以每天在21点55分定时执行为例子来演示每天执行一次
*
* @author super先生
* @datetime 2023/4/8 16:00
*/
@Scheduled(cron = "0 55 21 * * ? ")
public void testPerDayOn22Clock() {
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = simpleDateFormat.format(new Date());
System.out.println("每天定时21点55分输出timer信息,输出时间:" + format);
}
}
输出结果如下图所示:
5. 文末总结
cron
表达式有很多情形,比如每隔2分钟执行一次,定时在每天的13点执行一次等。
这都需要你在实际工作中,按照项目的需求来使用不同的cron
表达式,以满足项目上的需要。
我曾在工作中就写过,在每天凌晨3点执行一次定时任务,来统计昨日的资金收入。