1. 概述
使用cron调度器,我们可以自动化处理那些原本需要手动执行的重复性任务。此外,cron表达式能让我们精确指定任务的执行时间。
在Java中调度任务时,我们通常使用Quartz库——这是一个完全用Java编写的开源任务调度解决方案。如果使用Spring框架,还可以通过@Scheduled注解轻松实现任务调度。
虽然cron表达式功能强大,但其语法有时会让人困惑甚至望而生畏。
本文将深入探讨cron表达式中星号(*)和问号(?)这两个特殊字符的区别。
2. Cron表达式中的字段
在深入讨论前,先了解cron表达式中的字段结构。
在Quartz中,cron表达式是一个由空格分隔的字符串,最多包含七个字段,每个字段代表特定的日期时间单位:
字段 | 必需 | 允许值 | 允许的特殊字符 |
---|---|---|---|
秒(Seconds) | 是 | 0-59 | , - * / |
分(Minutes) | 是 | 0-59 | , - * / |
时(Hours) | 是 | 0-23 | , - * / |
日(Day of Month) | 是 | 1-31 | , - * / ? L W |
月(Month) | 是 | 0-11 (或 JAN-DEC) | , - * / |
周(Day of Week) | 是 | 1-7 (或 SUN-SAT) | , - * / ? L C # |
年(Year) | 否 | 1970-2099 (或空) | , - * / |
如上表所示,除年份字段外,所有字段都是必需的。若未指定年份,任务将每年执行。
Unix cron表达式的语法略有不同:
字段 | 必需 | 允许值 | 允许的特殊字符 |
---|---|---|---|
分(Minutes) | 是 | 0-59 | , - * / |
时(Hours) | 是 | 0-23 | , - * / |
日(Day of Month) | 是 | 1-31 | , - * / |
月(Month) | 是 | 1-12 (或 JAN-DEC) | , - * / |
周(Day of Week) | 是 | 0-6 (或 SUN-SAT) | , - * / |
Unix cron表达式由五个字段组成,后接要执行的命令。与Quartz不同,它没有秒和年字段,专注于当前年度的任务调度。
⚠️ 注意:Unix cron表达式不允许使用问号(?)符号。
后续内容将主要围绕Quartz库的cron表达式展开。
3. 问号(?)在Cron表达式中的用法
现在来看问号(?)在cron表达式中的作用。简单来说,它表示不指定具体值。
✅ 只能用于"日(Day of Month)"和"周(Day of Week)"字段。
但需注意:日字段和周字段是互斥的。也就是说,不能在同一个表达式中同时为这两个字段指定值。
例如,以下表达式会报错:
0 30 10 1 OCT 2 2023
为便于理解,用表格展示:
秒 | 分 | 时 | 日 | 月 | 周 | 年 |
---|---|---|---|---|---|---|
0 | 30 | 10 | 1 | OCT | 2 | 2023 |
我们同时设置了日字段和周字段,这在Quartz中是不支持的。
即使日期恰好匹配正确的星期几,表达式仍然无效:
0 30 10 30 OCT 2 2023
2023年10月30日确实是星期一,但该表达式依然非法。
✅ 由于必须为这两个字段设置值,我们需要在其中之一使用问号(?)表示未设置。 使用问号的字段将被忽略:
0 0 0 30 OCT ?
这个例子中,任务将在每年10月30日的午夜执行。
❌ 此外,问号(?)在单个cron表达式中只能出现一次。 同时使用两个问号也会报错:
0 30 * ? OCT ?
4. 星号(*)在Cron表达式中的用法
星号(*)在cron表达式中表示所有可能的值。换句话说,用它来指定某个字段的所有允许值。
✅ 与问号不同,星号可用于cron表达式中的任何字段。
例如,创建一个在小时字段使用星号的表达式:
0 30 * 1 OCT ?
用表格展示:
秒 | 分 | 时 | 日 | 月 | 周 | 年 |
---|---|---|---|---|---|---|
0 | 30 | * | 1 | OCT | ? | 空 |
该任务在10月1日的每个小时(0-23时)的30分0秒执行。
星号也可用于多个字段:
* * * * OCT ?
这个任务在10月的每秒都会执行。
4.1. Linux Cron中的日和周字段
在Linux cron中,日字段和周字段的行为与Quartz不同:
- 它们不是互斥的,可以在同一个表达式中同时设置值。
- 当两个字段都包含非星号值时,它们形成"并集"关系:
这个任务在10月1日和每个星期五的10:30执行。30 10 1 10 5
- 当其中一个字段以星号开头时,它们形成"交集"关系:
这个任务只在星期一的每天(即每个星期一)的10:30执行。30 10 */1 * 1
5. 星号(*)和问号(?)的对比
总结一下两个特殊字符的主要区别:
特性 | 星号 (*) | 问号 (?) |
---|---|---|
含义 | 表示字段的所有允许值 | 表示不指定具体值 |
可用字段 | 任何字段 | 仅限日和周字段 |
使用目的 | 指定字段的所有值 | 将字段留空 |
出现次数 | 同一表达式可多次出现 | 每个表达式只能出现一次 |
6. 结论
本文深入探讨了cron表达式中星号(*)和问号(?)的区别。
简单粗暴地总结:
- ✅ 星号(*)用于指定某个字段的所有允许值
- ✅ 问号(?)表示不指定值,且只能用于日和周字段
由于Quartz不支持同时设置日和周字段,我们必须在其中之一使用问号(?)来留空。这个设计虽然有点反直觉,但理解后就能避免常见的调度踩坑问题。