Java 中如何运行定时(cron)任务?
Java 跑 cron 定时任务的主流方案:Quartz Scheduler 完整示例(Maven 依赖、Job、cron 触发器)、Spring @Scheduled 简法、ScheduledExecutorService 轻量替代与 FAQ。
功能完整的方案用 Quartz Scheduler:加依赖 → 写一个实现 Job 接口的类 → 用 CronScheduleBuilder 绑定 cron 表达式启动调度器。 如果只是 Spring 项目里的简单定时需求,@Scheduled(cron = "...") 一行注解更省事。
方案一:Quartz Scheduler
1. 加 Maven 依赖
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
2. 定义 Job
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext context) {
System.out.println("Hello world! " + LocalDateTime.now());
}
}
3. 用 cron 表达式调度
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
JobDetail job = JobBuilder.newJob(HelloJob.class)
.withIdentity("helloJob", "group1")
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("helloTrigger", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 * * * * ?")) // 每分钟
.build();
scheduler.scheduleJob(job, trigger);
注意 Quartz 的表达式是六/七字段(秒 分 时 日 月 星期 [年]),和 Linux crontab 的五字段不同:0 * * * * ? 表示每分钟第 0 秒。
Quartz 还支持 JDBC JobStore 持久化调度、集群部署、misfire 补偿——企业级定时任务平台的基本盘。
方案二:Spring 的 @Scheduled(更常用)
Spring 项目里大多数场景不需要 Quartz 的重量级能力:
@Component
public class ReportTask {
@Scheduled(cron = "0 30 2 * * ?") // 每天 2:30
public void dailyReport() {
// ...
}
}
启动类加 @EnableScheduling。固定间隔用 @Scheduled(fixedRate = 300_000)(毫秒)。同样是六字段格式。
方案三:标准库轻量替代
只要"每 N 秒/分钟跑一次",不需要日历语义时:
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(() -> doWork(), 0, 5, TimeUnit.MINUTES);
没有 cron 表达式,但零依赖、行为可控。
观测云对照
JVM 内的调度器和系统 cron 一样有"静默不跑"的风险,而且排查更难——线程池耗尽、上次执行卡死都会让后续任务无声延误。接入观测云 APM 后,可把定时任务方法埋点为 span,观测每次执行耗时与异常;任务日志统一采集后,用监控器做"预期窗口内无成功日志"的心跳告警,JVM 停顿、线程池打满导致的任务延误都能被兜住。
常见问题(FAQ)
Q:Quartz 表达式里的 ? 是什么?
A:? 表示"不指定",只能用在"日"和"星期"两个字段之一上——因为这两个字段同时指定会互相冲突。0 30 2 ? * MON 这类写法就是靠 ? 让位。
Q:fixedRate 和 fixedDelay 有什么区别?
A:fixedRate 按固定频率触发(不管上一次是否结束,可能并发);fixedDelay 是上一次结束后再等固定间隔。怕重叠用后者,或加锁/状态位。
Q:多实例部署时任务会重复执行吗?
A:会。@Scheduled 没有集群协调,每个实例都会触发。要么 Quartz + JDBC 集群模式,要么 ShedLock 之类加分布式锁。