Cron 表达式的格式是怎样的?如何使用?
简化版
Cron 表达式用来定义定时任务的触发时间规则。Quartz/Spring 的 Cron 是 6 位(或 7 位),从左到右依次是:秒 分 时 日 月 周(年)。每个位置可以用具体数字、或特殊字符:*(任意值)、?(不指定,用于日和周互斥)、-(范围)、,(枚举)、/(步长)、L(最后)、#(第几个)。例如 0 0 2 * * ? = 每天凌晨 2 点整、0 0/30 * * * ? = 每 30 分钟、0 0 9 ? * MON-FRI = 周一到周五早上 9 点。关键:「日」和「周」不能同时指定(会冲突),其中一个要用 ?。
详细版
Quartz Cron 6 位格式(Spring 也用这个):
秒 分 时 日 月 周
* * * * * ?
0-59 0-59 0-23 1-31 1-12 1-7或SUN-SAT
(注意:Linux crontab 是 5 位——分 时 日 月 周,没有秒;Quartz 是 6 位有秒。别搞混。)
特殊字符:
| 字符 | 含义 | 例子 |
|---|---|---|
* | 任意值 | 每一个 |
? | 不指定(仅日/周) | 日和周互斥时用 |
- | 范围 | MON-FRI 周一到周五 |
, | 枚举 | 1,15 第1和15 |
/ | 步长 | 0/30 从0开始每30 |
L | 最后 | L 月末、6L 最后一个周五 |
# | 第几个 | 6#3 第三个周五 |
W | 最近工作日 | 15W 最近15号的工作日 |
常见例子:
0 0 2 * * ? 每天凌晨 2 点
0 0/30 * * * ? 每 30 分钟
0 0 9 ? * MON-FRI 周一到周五早上 9 点
0 0 0 1 * ? 每月 1 号 0 点
0 0 0 L * ? 每月最后一天 0 点
0 15 10 ? * 6#3 每月第三个周五 10:15
完整版教学
一、Cron 表达式是什么
Cron 表达式是用一串字符精确描述定时任务触发时间规律的方式。定时任务框架(Quartz、Spring @Scheduled、XXL-JOB)都用 Cron 表达式来配置「任务什么时候执行」。它非常灵活,能表达从「每秒」到「每月最后一个工作日」这样复杂的时间规则。掌握 Cron 表达式是配置定时任务的基本功。
二、格式:6 位(Quartz)vs 5 位(Linux)
要先分清两种 Cron 格式,别搞混:
- Quartz / Spring 的 Cron:6 位(可选第 7 位年),从左到右:秒 分 时 日 月 周(年)。
- Linux crontab 的 Cron:5 位,从左到右:分 时 日 月 周(没有秒)。
面试和 Java 开发主要用 Quartz 的 6 位格式。各位置的取值范围:
- 秒:0-59
- 分:0-59
- 时:0-23
- 日:1-31
- 月:1-12(或 JAN-DEC)
- 周:1-7(1=周日…7=周六,Quartz)或 SUN-SAT
三、特殊字符详解
Cron 的强大在于特殊字符,让时间规则能灵活表达:
*(星号):任意值/每一个。在「分」位置就是「每分钟都触发」。?(问号):不指定值,只能用在**「日」和「周」**这两个位置。用于解决日和周的冲突(见下节)。-(连字符):范围。MON-FRI表示周一到周五,10-12表示 10、11、12。,(逗号):枚举多个值。1,15表示 1 号和 15 号。/(斜杠):步长/间隔。0/30在分位置表示「从 0 分开始,每 30 分钟」(即 0 分、30 分)。L(Last):最后。在「日」位置L表示月末最后一天;在「周」位置6L表示当月最后一个周五。#(井号):第几个。在「周」位置6#3表示当月第三个周五。W:最近的工作日。15W表示离每月 15 号最近的工作日(周末则顺延到最近的周一/周五)。
四、关键坑:日和周互斥
Cron 里最容易踩的坑是——「日」和「周」两个位置不能同时指定具体值,其中一个必须用 ?。
原因:「日」和「周」会互相冲突。想想 0 0 0 15 * 1(每月 15 号 且 每周一 0 点)——15 号和周一大部分时候不是同一天,同时限定会产生歧义。所以 Quartz 规定:这两个位置,你指定其中一个、另一个就必须用 ?(表示不指定):
- 想「每月 15 号触发」(不管星期几):
0 0 0 15 * ?(周用?)。 - 想「每周一触发」(不管几号):
0 0 0 ? * MON(日用?)。
记住:日和周,指定一个、另一个用 ?。 写错(都用 * 或都指定)会报错或行为不符预期。
五、常见表达式解读
多看几个常用例子,培养语感(6 位:秒 分 时 日 月 周):
0 0 2 * * ?→ 每天凌晨 2:00:00(秒 0、分 0、时 2、日任意、月任意、周不指定)。0 0/30 * * * ?→ 每 30 分钟(分从 0 开始每 30)。0 0 9 ? * MON-FRI→ 周一到周五每天 9:00(日不指定、周一到周五)。0 0 0 1 * ?→ 每月 1 号 0 点。0 0 0 L * ?→ 每月最后一天 0 点。0 0 12 ? * SUN→ 每周日中午 12 点。0 15 10 ? * 6#3→ 每月第三个周五 10:15。*/10 * * * * ?→ 每 10 秒。
六、常见误区与追问
这道题面试时最容易丢分的地方,是把「Cron 表达式」答成一段泛泛的组件介绍。更稳的答法是先给结论,再沿着 分布式任务调度链路 拆清楚流程,最后补上异常场景、数字边界和选型取舍。
| 回答层次 | 要讲清的内容 | 容易漏掉的边界 |
|---|---|---|
| 核心定义 | Cron 用字段表达周期触发时间,面试要说清秒、分、时、日、月、周以及问号、星号、斜杠等含义 | 不要停在名词解释 |
| 流程机制 | 解析秒分时日月周字段 -> 计算下一次触发时间 -> 到点生成触发事件 -> 提交给执行器 -> 执行完成后计算下一轮 | 说明谁触发、谁存储、谁通知、谁兜底 |
| 工程取舍 | 0 0/5 * * * ? 表示从每小时第 0 分钟开始每 5 分钟触发一次;0 0 2 * * ? 表示每天 2 点触发 | 调度系统解决统一触发和治理,业务侧仍要保证幂等,因为网络抖动和重试不可避免 |
Cron 表达式 面试拆解:
1. 解析秒分时日月周字段
2. 计算下一次触发时间
3. 到点生成触发事件
4. 提交给执行器
5. 执行完成后计算下一轮
记忆钩子:先区分调度中心、执行器、触发、路由、分片、重试和告警,再说明如何避免重复执行;回答时一定要落到题目中的「Cron 表达式」,不要把相邻中间件的能力混着讲。
- 误区:Cron 所有框架字段数都一样。 Linux crontab 常见 5 字段,Quartz 常见 6 或 7 字段,含秒和可选年。
- 误区:日和周可以随便同时指定。 Quartz 中日和周通常一个指定另一个用问号,避免语义冲突。
- 误区:Cron 能保证任务不会重叠。 Cron 只负责触发时间,任务耗时超过周期时是否并发要靠调度框架控制。
- 追问:星号和问号区别是什么? 星号表示任意值,问号表示不指定,常用于日和周字段互斥。
- 追问:斜杠表达什么? 表示步长,例如 0/10 在秒字段表示从 0 秒开始每 10 秒。
- 追问:如何避免写错 Cron? 用工具预览未来触发时间,并在测试环境验证时区和字段数。
七、加强记忆
Cron 表达式定义定时任务触发时间。Quartz/Spring 是 6 位(秒 分 时 日 月 周,可选年),Linux crontab 是 5 位(分 时 日 月 周,无秒)——别搞混。特殊字符:*(任意)、?(不指定,仅日/周)、-(范围如 MON-FRI)、,(枚举)、/(步长如 0/30 每30)、L(最后:月末/最后一个周几)、#(第几个如 6#3 第三个周五)、W(最近工作日)。最大的坑:日和周互斥——不能同时指定具体值,指定一个、另一个必须用 ?(每月 15 号 0 0 0 15 * ?、每周一 0 0 0 ? * MON)。常用:0 0 2 * * ?(每天2点)、0 0/30 * * * ?(每30分)、0 0 9 ? * MON-FRI(工作日9点)、0 0 0 L * ?(每月最后一天)。口诀:6 位有秒(Quartz)、日和周指定一个另一个用 ?、/步长 L最后 #第几个。