Java的任务调度框架之 Quartz 以及 CronTrigger,CronScheduleBuilder 和 Cron表达式 笔记250930
Java任务调度框架Quartz:CronTrigger、CronScheduleBuilder与Cron表达式详解
1. Quartz框架核心概念
1.1 Quartz架构概览
Quartz是一个功能完善的开源作业调度框架,提供精确的任务调度能力,支持从简单定时任务到复杂企业级调度的各种场景。
1.2 核心组件关系
Scheduler (调度器)
↓
Trigger (触发器) → CronTrigger (Cron触发器)
↓
CronScheduleBuilder (Cron调度构建器)
↓
Cron Expression (Cron表达式)
2. Cron表达式深度解析
2.1 标准格式与字段说明
秒 分 时 日 月 星期 [年]
字段详细说明表:
| 位置 | 字段 | 允许值 | 特殊字符 | 示例说明 |
|---|---|---|---|---|
| 1 | 秒 | 0-59 | , - * / | 0,30 = 0秒和30秒时触发 |
| 2 | 分 | 0-59 | , - * / | 0/15 = 从0分开始每15分钟 |
| 3 | 时 | 0-23 | , - * / | 9-17 = 9点到17点之间 |
| 4 | 日 | 1-31 | , - * ? / L W | L = 最后一天,15W = 最接近15日的工作日 |
| 5 | 月 | 1-12 或 JAN-DEC | , - * / | JAN,MAR,MAY = 1月、3月、5月 |
| 6 | 星期 | 1-7 或 SUN-SAT | , - * ? / L # | 2#3 = 第3个星期一,6L = 最后一个星期五 |
| 7 | 年 (可选) | 1970-2099 | , - * / | 2024-2025 = 2024和2025年 |
2.2 特殊字符完全指南
基础字符
// * - 所有值
"* * * * * ?" // 每秒执行
"0 * * * * ?" // 每分钟的0秒执行
// ? - 不指定值(避免冲突)
"0 0 12 * * ?" // 每天12点,不指定具体日期
"0 0 12 ? * MON" // 每周一12点,不指定具体日期
// - - 范围
"0 0 9-17 * * ?" // 9点到17点每小时执行
"0 15-30 * * * ?" // 每小时的15-30分执行
// , - 列举值
"0 0 6,12,18 * * ?" // 每天6点、12点、18点执行
"0 0 9 ? * MON,WED,FRI" // 周一、三、五9点执行
// / - 增量
"0/10 * * * * ?" // 从0秒开始每10秒执行
"0 5/30 * * * ?" // 从5分开始每30分钟执行
高级字符
// L - 最后
"0 0 23 L * ?" // 每月最后一天23点执行
"0 0 12 ? * L" // 每周六执行(L在星期字段=周六)
// W - 工作日
"0 0 9 10W * ?" // 最接近10号的工作日9点执行
"0 0 18 LW * ?" // 每月最后一个工作日18点执行
// # - 第几个
"0 0 9 ? * 2#2" // 每月第2个周一9点执行
"0 0 12 ? * 6#1,6#3" // 每月第1个和第3个周五12点执行
3. CronScheduleBuilder详解
3.1 核心构建方法
public class CronScheduleBuilderDemo {
public static void main(String[] args) {
// 1. 基础Cron表达式构建
CronScheduleBuilder.cronSchedule("0 0 12 * * ?");
// 2. 每日固定时间
CronScheduleBuilder.dailyAtHourAndMinute(9, 30); // 每天9:30
// 3. 每周固定时间
CronScheduleBuilder.weeklyOnDayAndHourAndMinute(
DateBuilder.MONDAY, 10, 0); // 每周一10:00
// 4. 每月固定日期
CronScheduleBuilder.monthlyOnDayAndHourAndMinute(
15, 14, 30); // 每月15号14:30
}
}
3.2 高级配置方法
public class AdvancedCronScheduleBuilder {
public static CronScheduleBuilder createAdvancedSchedule() {
return CronScheduleBuilder.cronSchedule("0 0/15 9-17 ? * MON-FRI")
// 设置时区
.inTimeZone(TimeZone.getTimeZone("Asia/Shanghai"))
// 错过触发处理策略
.withMisfireHandlingInstructionDoNothing()
// 或者使用其他策略:
// .withMisfireHandlingInstructionFireAndProceed()
// .withMisfireHandlingInstructionIgnoreMisfires();
}
}
3.3 错过触发处理策略
public class MisfireHandlingExamples {
public static void main(String[] args) {
// 1. 忽略错过触发,立即执行并恢复正常调度
CronScheduleBuilder.cronSchedule("0 0 12 * * ?")
.withMisfireHandlingInstructionIgnoreMisfires();
// 2. 立即执行一次错过触发,然后恢复正常调度(默认)
CronScheduleBuilder.cronSchedule("0 0 12 * * ?")
.withMisfireHandlingInstructionFireAndProceed();
// 3. 不执行错过触发,等待下次正常调度
CronScheduleBuilder.cronSchedule("0 0 12 * * ?")
.withMisfireHandlingInstructionDoNothing();
}
}
4. CronTrigger深度解析
4.1 创建CronTrigger的多种方式
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.util.TimeZone;
public class CronTriggerCreationDemo {
public static class DemoJob implements Job {
@Override
public void execute(JobExecutionContext context) {
System.out.println("任务执行: " + new java.util.Date());
}
}
public static void main(String[] args) throws SchedulerException {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
JobDetail job = JobBuilder.newJob(DemoJob.class)
.withIdentity("demoJob", "group1")
.build();
// 方式1:基础CronTrigger
CronTrigger trigger1 = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))
.build();
// 方式2:带时区的CronTrigger
CronTrigger trigger2 = TriggerBuilder.newTrigger()
.withIdentity("trigger2", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 9 * * ?")
.inTimeZone(TimeZone.getTimeZone("America/New_York")))
.build();
// 方式3:完整配置的CronTrigger
CronTrigger trigger3 = TriggerBuilder.newTrigger()
.withIdentity("trigger3", "group1")
.withDescription("完整配置的Cron触发器示例")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/30 9-17 ? * MON-FRI")
.inTimeZone(TimeZone.getTimeZone("Asia/Shanghai"))
.withMisfireHandlingInstructionDoNothing())
.startAt(DateBuilder.todayAt(8, 0, 0)) // 开始时间
.endAt(DateBuilder.tomorrowAt(18, 0, 0)) // 结束时间
.withPriority(7) // 优先级
.build();
scheduler.scheduleJob(job, trigger1);
}
}
4.2 CronTrigger属性获取
public class CronTriggerInspector {
public static void inspectTrigger(CronTrigger trigger) {
System.out.println("=== CronTrigger 详细信息 ===");
System.out.println("触发器Key: " + trigger.getKey());
System.out.println("描述: " + trigger.getDescription());
System.out.println("Cron表达式: " + trigger.getCronExpression());
System.out.println("表达式摘要: " + trigger.getExpressionSummary());
System.out.println("时区: " + trigger.getTimeZone().getID());
System.out.println("开始时间: " + trigger.getStartTime());
System.out.println("结束时间: " + trigger.getEndTime());
System.out.println("下次触发时间: " + trigger.getNextFireTime());
System.out.println("最终触发时间: " + trigger.getFinalFireTime());
System.out.println("优先级: " + trigger.getPriority());
// 获取错过触发策略
System.out.println("错过触发指令: " +
trigger.getMisfireInstruction());
}
}
5. 完整实战示例
5.1 企业级任务调度系统
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.text.SimpleDateFormat;
import java.util.*;
public class EnterpriseSchedulingSystem {
// 业务任务:邮件发送
public static class EmailJob implements Job {
@Override
public void execute(JobExecutionContext context) {
JobDataMap data = context.getMergedJobDataDataMap();
String to = data.getString("to");
String subject = data.getString("subject");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.printf("[%s] 发送邮件: %s - %s%n",
sdf.format(new Date()), to, subject);
}
}
// 业务任务:数据备份
public static class BackupJob implements Job {
@Override
public void execute(JobExecutionContext context) {
JobDataMap data = context.getMergedJobDataMap();
String database = data.getString("database");
System.out.printf("开始备份数据库: %s%n", database);
// 模拟备份操作
try {
Thread.sleep(3000);
System.out.println("备份完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 业务任务:报表生成
public static class ReportJob implements Job {
@Override
public void execute(JobExecutionContext context) {
JobDataMap data = context.getMergedJobDataMap();
String reportType = data.getString("reportType");
System.out.printf("生成%s报表%n", reportType);
// 模拟报表生成
try {
Thread.sleep(5000);
System.out.println("报表生成完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws SchedulerException {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
System.out.println("=== 企业级任务调度系统启动 ===");
// 1. 邮件通知任务
scheduleEmailNotification(scheduler);
// 2. 数据备份任务
scheduleDataBackup(scheduler);
// 3. 报表生成任务
scheduleReportGeneration(scheduler);
// 4. 系统监控任务
scheduleSystemMonitoring(scheduler);
// 运行演示
try {
Thread.sleep(300000); // 运行5分钟
} catch (InterruptedException e) {
e.printStackTrace();
}
scheduler.shutdown(true);
System.out.println("=== 系统关闭 ===");
}
private static void scheduleEmailNotification(Scheduler scheduler)
throws SchedulerException {
JobDetail job = JobBuilder.newJob(EmailJob.class)
.withIdentity("emailNotification", "notifications")
.usingJobData("to", "all@company.com")
.usingJobData("subject", "每日工作提醒")
.build();
// 使用CronScheduleBuilder创建工作日内每2小时发送
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("emailTrigger", "notifications")
.withDescription("工作时间内每2小时发送通知邮件")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 9-17/2 ? * MON-FRI")
.inTimeZone(TimeZone.getTimeZone("Asia/Shanghai"))
.withMisfireHandlingInstructionFireAndProceed())
.build();
scheduler.scheduleJob(job, trigger);
System.out.println("✓ 邮件通知任务已调度");
}
private static void scheduleDataBackup(Scheduler scheduler)
throws SchedulerException {
JobDetail job = JobBuilder.newJob(BackupJob.class)
.withIdentity("dataBackup", "maintenance")
.usingJobData("database", "production_db")
.build();
// 使用CronScheduleBuilder的便捷方法创建每日备份
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("backupTrigger", "maintenance")
.withDescription("每日凌晨执行数据备份")
.withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(2, 30))
.build();
scheduler.scheduleJob(job, trigger);
System.out.println("✓ 数据备份任务已调度");
}
private static void scheduleReportGeneration(Scheduler scheduler)
throws SchedulerException {
JobDetail job = JobBuilder.newJob(ReportJob.class)
.withIdentity("reportGeneration", "reporting")
.usingJobData("reportType", "销售日报")
.build();
// 复杂Cron表达式:工作日9点、14点生成报表
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("reportTrigger", "reporting")
.withDescription("工作日生成销售报表")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 9,14 ? * MON-FRI")
.withMisfireHandlingInstructionDoNothing())
.build();
scheduler.scheduleJob(job, trigger);
System.out.println("✓ 报表生成任务已调度");
}
private static void scheduleSystemMonitoring(Scheduler scheduler)
throws SchedulerException {
JobDetail job = JobBuilder.newJob(DemoJob.class)
.withIdentity("systemMonitor", "monitoring")
.build();
// 高频率监控:每30秒执行一次
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("monitorTrigger", "monitoring")
.withDescription("系统监控")
.withSchedule(CronScheduleBuilder.cronSchedule("0/30 * * * * ?"))
.build();
scheduler.scheduleJob(job, trigger);
System.out.println("✓ 系统监控任务已调度");
}
}
5.2 Cron表达式生成与验证工具
import org.quartz.CronExpression;
import java.text.ParseException;
import java.util.*;
public class CronExpressionToolkit {
/**
* 验证Cron表达式有效性
*/
public static ValidationResult validateExpression(String cronExpression) {
ValidationResult result = new ValidationResult();
try {
CronExpression cron = new CronExpression(cronExpression);
result.isValid = true;
result.expression = cron;
// 分析表达式结构
String[] fields = cronExpression.split(" ");
result.fieldCount = fields.length;
result.hasYearField = fields.length > 6;
} catch (ParseException e) {
result.isValid = false;
result.errorMessage = e.getMessage();
}
return result;
}
/**
* 获取未来执行时间预测
*/
public static List<Date> getExecutionForecast(String cronExpression,
int count,
TimeZone timeZone) {
List<Date> executions = new ArrayList<>();
try {
CronExpression cron = new CronExpression(cronExpression);
if (timeZone != null) {
cron.setTimeZone(timeZone);
}
Date currentTime = new Date();
for (int i = 0; i < count; i++) {
currentTime = cron.getNextValidTimeAfter(currentTime);
if (currentTime == null) break;
executions.add(currentTime);
}
} catch (ParseException e) {
System.err.println("无效的Cron表达式: " + e.getMessage());
}
return executions;
}
/**
* 生成常用Cron表达式模板
*/
public static Map<String, String> getCommonTemplates() {
Map<String, String> templates = new LinkedHashMap<>();
templates.put("每分钟执行", "0 * * * * ?");
templates.put("每5分钟执行", "0 0/5 * * * ?");
templates.put("每小时执行", "0 0 * * * ?");
templates.put("每天9点执行", "0 0 9 * * ?");
templates.put("工作日9-17点", "0 0 9-17 ? * MON-FRI");
templates.put("每周一9点", "0 0 9 ? * MON");
templates.put("每月1号", "0 0 6 1 * ?");
templates.put("每月最后一天", "0 0 18 L * ?");
return templates;
}
/**
* 详细分析Cron表达式
*/
public static void analyzeExpression(String cronExpression) {
System.out.println("=== Cron表达式详细分析 ===");
System.out.println("表达式: " + cronExpression);
ValidationResult result = validateExpression(cronExpression);
if (!result.isValid) {
System.out.println("❌ 无效表达式");
System.out.println("错误: " + result.errorMessage);
provideSuggestions(cronExpression, result.errorMessage);
return;
}
System.out.println("✅ 表达式有效");
System.out.println("字段数量: " + result.fieldCount);
System.out.println("包含年份字段: " + result.hasYearField);
// 显示未来执行时间
List<Date> nextExecutions = getExecutionForecast(cronExpression, 5, null);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("未来5次执行时间:");
for (int i = 0; i < nextExecutions.size(); i++) {
System.out.printf(" %d. %s%n", i + 1, sdf.format(nextExecutions.get(i)));
}
}
private static void provideSuggestions(String cronExpression, String error) {
System.out.println("💡 修复建议:");
if (error.contains("Day-of-Week") && error.contains("Day-of-Month")) {
System.out.println(" - 日字段和周字段冲突,将其中一个改为 '?'");
String fixed = cronExpression.replaceFirst("(\\d+|\\*|L|W|C)", "?");
System.out.println(" 例如: " + fixed);
}
if (error.contains("range")) {
System.out.println(" - 数值超出允许范围,请检查各字段的取值范围");
System.out.println(" 秒(0-59), 分(0-59), 时(0-23), 日(1-31), 月(1-12), 周(1-7)");
}
if (error.contains("illegal character")) {
System.out.println(" - 包含非法字符,请检查特殊字符使用是否正确");
}
}
static class ValidationResult {
boolean isValid;
String errorMessage;
CronExpression expression;
int fieldCount;
boolean hasYearField;
}
public static void main(String[] args) {
// 测试各种表达式
String[] testExpressions = {
"0 0 12 * * ?", // 有效
"0 0 12 * * ? 2024", // 有效(带年份)
"0 60 * * * ?", // 无效(分钟超出范围)
"0 0 12 * * MON", // 无效(日和星期冲突)
"0 0/15 9-17 ? * MON-FRI" // 有效(复杂表达式)
};
for (String expr : testExpressions) {
analyzeExpression(expr);
System.out.println();
}
// 显示常用模板
System.out.println("=== 常用Cron表达式模板 ===");
Map<String, String> templates = getCommonTemplates();
for (Map.Entry<String, String> entry : templates.entrySet()) {
System.out.printf("%-15s: %s%n", entry.getKey(), entry.getValue());
}
}
}
6. 最佳实践与高级特性
6.1 性能优化建议
public class QuartzBestPractices {
// 1. 合理配置线程池
public static Properties getOptimizedProperties() {
Properties props = new Properties();
props.setProperty("org.quartz.threadPool.threadCount", "10");
props.setProperty("org.quartz.threadPool.threadPriority", "5");
props.setProperty("org.quartz.jobStore.misfireThreshold", "60000");
return props;
}
// 2. 使用有状态Job避免并发问题
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public static class StatefulJob implements Job {
@Override
public void execute(JobExecutionContext context) {
// 保证不会并发执行
}
}
// 3. 合理设置错过触发策略
public static CronScheduleBuilder getRobustSchedule() {
return CronScheduleBuilder.cronSchedule("0 0 9 * * ?")
.withMisfireHandlingInstructionDoNothing();
}
}
6.2 生产环境配置
# quartz.properties
org.quartz.scheduler.instanceName = ProductionScheduler
org.quartz.scheduler.instanceId = AUTO
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 25
org.quartz.threadPool.threadPriority = 5
org.quartz.jobStore.misfireThreshold = 60000
# 集群配置
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true
6.3 常见问题排查
public class TroubleshootingGuide {
public static void diagnoseScheduler(Scheduler scheduler)
throws SchedulerException {
System.out.println("=== 调度器诊断信息 ===");
System.out.println("调度器名称: " + scheduler.getSchedulerName());
System.out.println("调度器实例ID: " + scheduler.getSchedulerInstanceId());
System.out.println("是否启动: " + scheduler.isStarted());
System.out.println("是否关闭: " + scheduler.isShutdown());
// 检查正在执行的任务
List<JobExecutionContext> currentlyExecuting =
scheduler.getCurrentlyExecutingJobs();
System.out.println("当前执行的任务数量: " + currentlyExecuting.size());
for (JobExecutionContext context : currentlyExecuting) {
System.out.println("执行中: " + context.getJobDetail().getKey());
}
}
}
通过这份完整的指南,您应该能够:
- 深入理解Cron表达式的语法和语义
- 熟练使用CronScheduleBuilder创建复杂的调度规则
- 掌握CronTrigger的各种配置选项和高级特性
- 构建健壮的企业级任务调度系统
- 有效诊断和解决调度相关问题
Quartz框架结合CronTrigger和CronScheduleBuilder提供了强大而灵活的任务调度能力,是Java应用中实现定时任务的优选方案。
1️⃣ 参考 1
Quartz 任务调度框架详解
Quartz 框架概述
Quartz 是一个功能丰富的开源作业调度库,完全由 Java 编写,可以集成到各种 Java 应用中。
核心组件
- Scheduler - 调度器,核心控制组件
- Job - 作业接口,定义执行的任务
- JobDetail - 作业实例的详细信息
- Trigger - 触发器,定义作业执行时机
- JobStore - 作业存储方式
CronTrigger 详解
CronTrigger 特点
- 基于日历的触发器
- 使用 Cron 表达式定义复杂调度规则
- 支持时区设置
- 支持错过触发策略
创建 CronTrigger
基本创建方式
import org.quartz.*;
// 使用 TriggerBuilder 创建
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?"))
.build();
// 或者直接创建
CronTrigger trigger = newTrigger()
.withIdentity("triggerName", "group1")
.withSchedule(cronSchedule("0 0 9 * * ?")) // 每天9点
.build();
CronScheduleBuilder 详解
主要构建方法
1. 基础构建
// 使用 Cron 表达式构建
CronScheduleBuilder cronSchedule = CronScheduleBuilder.cronSchedule("0 0/30 * * * ?");
// 每日特定时间
CronScheduleBuilder.dailyAtHourAndMinute(9, 30); // 每天9:30
// 每周特定时间
CronScheduleBuilder.weeklyOnDayAndHourAndMinute(DateUtil.MONDAY, 10, 0); // 每周一10:00
// 每月特定日期和时间
CronScheduleBuilder.monthlyOnDayAndHourAndMinute(15, 14, 30); // 每月15号14:30
2. 时区设置
TimeZone timeZone = TimeZone.getTimeZone("Asia/Shanghai");
CronScheduleBuilder cronSchedule = CronScheduleBuilder.cronSchedule("0 0 12 * * ?")
.inTimeZone(timeZone);
3. 错过触发策略
CronScheduleBuilder cronSchedule = CronScheduleBuilder.cronSchedule("0 0 9 * * ?")
.withMisfireHandlingInstructionIgnoreMisfires() // 忽略所有错过,立即执行
.withMisfireHandlingInstructionFireAndProceed() // 立即执行一次,然后按计划
.withMisfireHandlingInstructionDoNothing(); // 什么都不做,等待下次
Cron 表达式在 Quartz 中的使用
Quartz Cron 表达式格式
秒 分 时 日 月 周 年(可选)
特殊字符增强 Quartz 在标准 Cron 表达式基础上增加了特殊字符:
L 字符的增强用法
// 最后一天
"0 0 12 L * ?" // 每月最后一天12:00
"0 0 12 ? * L" // 每周最后一天(周六)12:00
"0 0 12 ? * 3L" // 每月最后一个周二
// 最近工作日
"0 0 12 LW * ?" // 每月最后一个工作日
W 字符用法
// 最近工作日
"0 0 12 15W * ?" // 距离15号最近的工作日
"0 0 12 W * ?" // 每月第一个工作日
完整示例代码
1. 基础 Job 实现
public class SimpleJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
String jobName = dataMap.getString("jobName");
System.out.println("执行作业: " + jobName + " 时间: " + new Date());
// 业务逻辑
try {
// 模拟业务处理
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
2. 调度器配置和使用
public class QuartzSchedulerExample {
public static void main(String[] args) throws SchedulerException {
// 1. 创建调度器工厂
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
// 2. 创建 JobDetail
JobDetail job = JobBuilder.newJob(SimpleJob.class)
.withIdentity("dailyJob", "reportGroup")
.usingJobData("jobName", "每日报表生成")
.usingJobData("priority", 1)
.build();
// 3. 创建 CronTrigger
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("dailyTrigger", "reportGroup")
.withSchedule(CronScheduleBuilder
.cronSchedule("0 0 2 * * ?") // 每天凌晨2点
.withMisfireHandlingInstructionFireAndProceed()
.inTimeZone(TimeZone.getTimeZone("Asia/Shanghai")))
.build();
// 4. 调度作业
scheduler.scheduleJob(job, trigger);
// 5. 启动调度器
scheduler.start();
// 添加关闭钩子
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
scheduler.shutdown(true);
} catch (SchedulerException e) {
e.printStackTrace();
}
}));
}
}
3. 复杂调度示例
public class ComplexSchedulingExample {
public void setupComplexSchedules() throws SchedulerException {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 工作日调度:周一至周五 9:00-18:00 每30分钟
JobDetail workdayJob = JobBuilder.newJob(WorkdayJob.class)
.withIdentity("workdayJob", "business")
.build();
CronTrigger workdayTrigger = TriggerBuilder.newTrigger()
.withIdentity("workdayTrigger", "business")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/30 9-18 ? * MON-FRI"))
.build();
// 月末调度:每月最后一个工作日 18:00
JobDetail monthEndJob = JobBuilder.newJob(MonthEndJob.class)
.withIdentity("monthEndJob", "finance")
.build();
CronTrigger monthEndTrigger = TriggerBuilder.newTrigger()
.withIdentity("monthEndTrigger", "finance")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 18 LW * ?"))
.build();
// 季度调度:每季度第一个月1号 8:00
JobDetail quarterJob = JobBuilder.newJob(QuarterJob.class)
.withIdentity("quarterJob", "finance")
.build();
CronTrigger quarterTrigger = TriggerBuilder.newTrigger()
.withIdentity("quarterTrigger", "finance")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 8 1 1,4,7,10 ?"))
.build();
// 注册所有作业
scheduler.scheduleJob(workdayJob, workdayTrigger);
scheduler.scheduleJob(monthEndJob, monthEndTrigger);
scheduler.scheduleJob(quarterJob, quarterTrigger);
scheduler.start();
}
}
4. 动态调度管理
public class DynamicSchedulerManager {
private Scheduler scheduler;
public DynamicSchedulerManager() throws SchedulerException {
this.scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
}
// 添加新作业
public void addJob(String jobName, String group, String cronExpression, Class<? extends Job> jobClass)
throws SchedulerException {
JobDetail job = JobBuilder.newJob(jobClass)
.withIdentity(jobName, group)
.storeDurably()
.build();
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobName + "Trigger", group)
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
.build();
scheduler.scheduleJob(job, trigger);
}
// 更新作业调度
public void updateJobSchedule(String triggerName, String group, String newCronExpression)
throws SchedulerException {
TriggerKey triggerKey = new TriggerKey(triggerName, group);
CronTrigger newTrigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.withSchedule(CronScheduleBuilder.cronSchedule(newCronExpression))
.build();
scheduler.rescheduleJob(triggerKey, newTrigger);
}
// 暂停作业
public void pauseJob(String jobName, String group) throws SchedulerException {
JobKey jobKey = new JobKey(jobName, group);
scheduler.pauseJob(jobKey);
}
// 恢复作业
public void resumeJob(String jobName, String group) throws SchedulerException {
JobKey jobKey = new JobKey(jobName, group);
scheduler.resumeJob(jobKey);
}
// 删除作业
public void deleteJob(String jobName, String group) throws SchedulerException {
JobKey jobKey = new JobKey(jobName, group);
scheduler.deleteJob(jobKey);
}
}
配置说明
quartz.properties 配置
# <font size=5 color=black ><b>调度器配置</b></font>
org.quartz.scheduler.instanceName = MyScheduler
org.quartz.scheduler.instanceId = AUTO
# <font size=5 color=black ><b>线程池配置</b></font>
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
# <font size=5 color=black ><b>作业存储</b></font>
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
org.quartz.jobStore.misfireThreshold = 60000
# <font size=5 color=black ><b>插件配置</b></font>
org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingTriggerHistoryPlugin
最佳实践
1. 异常处理
public class RobustJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
// 业务逻辑
processBusiness();
} catch (Exception e) {
// 记录日志
log.error("作业执行失败", e);
// 根据异常类型决定重试策略
if (isRecoverable(e)) {
JobExecutionException jobException =
new JobExecutionException(e);
jobException.setRefireImmediately(true); // 立即重试
throw jobException;
} else {
// 不可恢复异常,停止作业
JobExecutionException jobException =
new JobExecutionException(e);
jobException.setUnscheduleAllTriggers(true);
throw jobException;
}
}
}
private boolean isRecoverable(Exception e) {
// 判断异常是否可恢复
return e instanceof TimeoutException ||
e instanceof NetworkException;
}
}
2. 作业数据管理
public class DataAwareJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDataMap dataMap = context.getMergedJobDataMap();
// 安全获取数据
String config = dataMap.getString("config");
Integer retryCount = dataMap.getInt("retryCount");
// 更新作业数据
dataMap.put("lastExecution", new Date());
dataMap.put("executionCount",
dataMap.getInt("executionCount", 0) + 1);
}
}
Quartz 提供了强大而灵活的调度功能,结合 CronTrigger 和 CronScheduleBuilder 可以满足各种复杂的调度需求。合理使用这些组件可以构建出稳定可靠的任务调度系统。
2️⃣ 参考 2
Java Quartz 任务调度框架详解
Quartz 框架概述
Quartz 是一个功能强大的开源作业调度框架,可以集成到任何 Java 应用中,从简单的独立应用到大型企业级系统。
核心组件
1. Scheduler 调度器,负责管理所有的调度任务
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
scheduler.start();
2. Job 任务接口,定义要执行的工作
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("Job executed at: " + new Date());
}
}
3. JobDetail 任务详情,包含任务的元数据
JobDetail job = JobBuilder.newJob(MyJob.class)
.withIdentity("myJob", "group1")
.usingJobData("jobSays", "Hello World!")
.build();
4. Trigger 触发器,定义任务执行的时间规则
CronTrigger 详解
创建 CronTrigger
// 方式1:使用 TriggerBuilder
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?"))
.build();
// 方式2:直接创建 CronTrigger
CronTrigger cronTrigger = TriggerBuilder.newTrigger()
.withIdentity("trigger2", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 12 * * ?"))
.forJob("myJob", "group1")
.build();
CronScheduleBuilder 详解
主要方法
1. 基础构建方法
// 从字符串创建
CronScheduleBuilder.cronSchedule("0 0 9 * * ?");
// 从 CronExpression 创建
CronExpression cronExpr = new CronExpression("0 0 12 * * ?");
CronScheduleBuilder.cronSchedule(cronExpr);
// 预定义调度
CronScheduleBuilder.dailyAtHourAndMinute(9, 30); // 每天9:30
CronScheduleBuilder.weeklyOnDayAndHourAndMinute(1, 9, 0); // 每周一9:00
2. 时区设置
CronScheduleBuilder.cronSchedule("0 0 12 * * ?")
.inTimeZone(TimeZone.getTimeZone("America/New_York"));
3. 失火策略
// 忽略失火指令,立即执行
CronScheduleBuilder.cronSchedule("0 0/5 * * * ?")
.withMisfireHandlingInstructionIgnoreMisfires();
// 立即触发并继续正常调度(默认)
CronScheduleBuilder.cronSchedule("0 0/5 * * * ?")
.withMisfireHandlingInstructionFireAndProceed();
// 不触发,等待下一次调度
CronScheduleBuilder.cronSchedule("0 0/5 * * * ?")
.withMisfireHandlingInstructionDoNothing();
完整示例
1. 基础调度示例
public class QuartzSchedulerExample {
public static void main(String[] args) throws SchedulerException {
// 创建调度器工厂
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
// 定义 Job
JobDetail job = JobBuilder.newJob(SimpleJob.class)
.withIdentity("simpleJob", "group1")
.usingJobData("executionCount", 0)
.build();
// 定义 Trigger - 每5分钟执行
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("simpleTrigger", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?"))
.startNow()
.build();
// 调度任务
scheduler.scheduleJob(job, trigger);
// 启动调度器
scheduler.start();
}
public static class SimpleJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
int count = dataMap.getInt("executionCount");
count++;
dataMap.put("executionCount", count);
System.out.println("Job executed! Count: " + count + " Time: " + new Date());
}
}
}
2. 复杂调度示例
public class AdvancedSchedulerExample {
public void scheduleComplexJob() throws SchedulerException {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 多个 Job 定义
JobDetail emailJob = JobBuilder.newJob(EmailJob.class)
.withIdentity("emailJob", "notificationGroup")
.build();
JobDetail reportJob = JobBuilder.newJob(ReportJob.class)
.withIdentity("reportJob", "reportGroup")
.usingJobData("reportType", "DAILY")
.build();
// 多个 Trigger 定义
// 工作时间内每2小时发送邮件
Trigger emailTrigger = TriggerBuilder.newTrigger()
.withIdentity("emailTrigger", "notificationGroup")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 9-17/2 * * ?")
.inTimeZone(TimeZone.getTimeZone("Asia/Shanghai")))
.build();
// 每天凌晨生成报告
Trigger reportTrigger = TriggerBuilder.newTrigger()
.withIdentity("reportTrigger", "reportGroup")
.withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(2, 30))
.build();
// 每月最后一天执行清理
Trigger cleanupTrigger = TriggerBuilder.newTrigger()
.withIdentity("cleanupTrigger", "maintenanceGroup")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 3 L * ?")
.withMisfireHandlingInstructionDoNothing())
.forJob("cleanupJob", "maintenanceGroup")
.build();
// 调度所有任务
scheduler.scheduleJob(emailJob, emailTrigger);
scheduler.scheduleJob(reportJob, reportTrigger);
scheduler.start();
}
}
3. Spring 集成示例
@Configuration
public class QuartzConfig {
@Bean
public JobDetail sampleJobDetail() {
return JobBuilder.newJob(SampleJob.class)
.withIdentity("sampleJob")
.storeDurably()
.build();
}
@Bean
public Trigger sampleJobTrigger() {
return TriggerBuilder.newTrigger()
.forJob(sampleJobDetail())
.withIdentity("sampleTrigger")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/10 * * * ?"))
.build();
}
@Bean
public SchedulerFactoryBean scheduler(Trigger... triggers) {
SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean();
schedulerFactory.setTriggers(triggers);
schedulerFactory.setJobDetails(sampleJobDetail());
return schedulerFactory;
}
}
Cron 表达式最佳实践
1. 表达式验证
public class CronValidator {
public static boolean isValidCronExpression(String cronExpression) {
try {
new CronExpression(cronExpression);
return true;
} catch (ParseException e) {
return false;
}
}
public static void validateAndSchedule(String cronExpression, JobDetail job)
throws SchedulerException, ParseException {
if (!isValidCronExpression(cronExpression)) {
throw new ParseException("Invalid cron expression: " + cronExpression, 0);
}
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
Trigger trigger = TriggerBuilder.newTrigger()
.forJob(job)
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
.build();
scheduler.scheduleJob(trigger);
}
}
2. 动态调度
public class DynamicScheduler {
private Scheduler scheduler;
public DynamicScheduler() throws SchedulerException {
this.scheduler = StdSchedulerFactory.getDefaultScheduler();
this.scheduler.start();
}
public void scheduleJob(String jobName, String group, String cronExpression,
Class<? extends Job> jobClass) throws SchedulerException {
JobDetail job = JobBuilder.newJob(jobClass)
.withIdentity(jobName, group)
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobName + "Trigger", group)
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
.build();
scheduler.scheduleJob(job, trigger);
}
public void rescheduleJob(String triggerName, String group, String newCronExpression)
throws SchedulerException {
TriggerKey triggerKey = new TriggerKey(triggerName, group);
Trigger oldTrigger = scheduler.getTrigger(triggerKey);
Trigger newTrigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.withSchedule(CronScheduleBuilder.cronSchedule(newCronExpression))
.build();
scheduler.rescheduleJob(triggerKey, newTrigger);
}
}
注意事项
1. 资源管理
public class SchedulerManager {
public void shutdownSchedulerGracefully(Scheduler scheduler)
throws SchedulerException {
// 等待当前执行的任务完成
scheduler.shutdown(true);
// 或者强制关闭
// scheduler.shutdown(false);
}
}
2. 异常处理
public class RobustJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
// 业务逻辑
performBusinessLogic();
} catch (Exception e) {
// 记录日志但不会影响其他任务
log.error("Job execution failed", e);
// 如果需要重新执行
JobExecutionException jobException = new JobExecutionException(e);
jobException.setRefireImmediately(false); // 不要立即重试
throw jobException;
}
}
}
3. 集群配置
# <font size=5 color=black ><b>quartz.properties</b></font>
org.quartz.scheduler.instanceName = MyClusteredScheduler
org.quartz.scheduler.instanceId = AUTO
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource = myDS
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true
org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/quartz
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = password
通过 Quartz 框架的 CronTrigger 和 CronScheduleBuilder,可以实现非常灵活和强大的任务调度功能,满足各种复杂的业务需求。
3️⃣ 参考 3
Java 任务调度框架 Quartz 详解
1. Quartz 框架概述
Quartz 是一个功能丰富的开源作业调度库,可以集成到任何 Java 应用中。它提供了简单但强大的调度功能,支持从简单的定时任务到复杂的多作业调度。
核心组件
- Scheduler - 调度器,核心控制组件
- Job - 作业接口,定义执行任务
- JobDetail - 作业实例的详细信息
- Trigger - 触发器,定义作业执行时间
- JobStore - 作业存储方式
2. CronTrigger 详解
CronTrigger 是基于日历的触发器,使用 Cron 表达式定义复杂的调度规则。
创建 CronTrigger 的方式
// 方式1:使用 TriggerBuilder
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?"))
.build();
// 方式2:直接创建 CronTrigger 实例
CronTrigger cronTrigger = newTrigger()
.withIdentity("trigger2", "group1")
.withSchedule(cronSchedule("0 0 12 * * ?"))
.build();
CronTrigger 重要属性
CronTrigger trigger = (CronTrigger) TriggerBuilder.newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 9 ? * MON-FRI"))
.startAt(DateBuilder.todayAt(9, 0, 0)) // 开始时间
.endAt(DateBuilder.todayAt(17, 0, 0)) // 结束时间
.withPriority(5) // 优先级
.build();
// 获取 Cron 表达式
String cronExpression = trigger.getCronExpression();
// 获取时区
TimeZone timeZone = trigger.getTimeZone();
// 获取下次触发时间
Date nextFireTime = trigger.getFireTimeAfter(new Date());
3. CronScheduleBuilder 详解
CronScheduleBuilder 是构建 CronTrigger 调度策略的建造器类。
主要方法
public class CronScheduleBuilderExample {
// 基础构建方法
public static void buildExamples() {
// 1. 使用 Cron 表达式
CronScheduleBuilder.cronSchedule("0 0/5 * * * ?");
// 2. 每天固定时间
CronScheduleBuilder.dailyAtHourAndMinute(9, 30); // 每天9:30
// 3. 每周特定时间
CronScheduleBuilder.weeklyOnDayAndHourAndMinute(
DateBuilder.MONDAY, 10, 0); // 每周一10:00
// 4. 每月特定日期和时间
CronScheduleBuilder.monthlyOnDayAndHourAndMinute(
15, 9, 0); // 每月15日9:00
}
// 复杂调度配置
public static Trigger buildComplexTrigger() {
return TriggerBuilder.newTrigger()
.withIdentity("complexTrigger", "group1")
.withSchedule(
CronScheduleBuilder.cronSchedule("0 0 12 * * ?")
.inTimeZone(TimeZone.getTimeZone("GMT+8")) // 设置时区
.withMisfireHandlingInstructionFireAndProceed() // misfire 处理策略
)
.build();
}
}
Misfire 处理策略
public class MisfireExamples {
public Trigger[] createTriggersWithMisfireHandling() {
Trigger[] triggers = new Trigger[4];
// 1. 忽略 misfire,按原计划执行
triggers[0] = TriggerBuilder.newTrigger()
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?")
.withMisfireHandlingInstructionIgnoreMisfires())
.build();
// 2. 立即执行一次,然后按原计划(默认策略)
triggers[1] = TriggerBuilder.newTrigger()
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?")
.withMisfireHandlingInstructionFireAndProceed())
.build();
// 3. 不立即执行,等待下次触发
triggers[2] = TriggerBuilder.newTrigger()
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?")
.withMisfireHandlingInstructionDoNothing())
.build();
return triggers;
}
}
4. Cron 表达式格式详解
完整格式
秒 分 时 日 月 周 年(可选)
字段详细说明
| 字段 | 必填 | 取值范围 | 特殊字符 |
|---|---|---|---|
| 秒 | 是 | 0-59 | , - * / |
| 分 | 是 | 0-59 | , - * / |
| 时 | 是 | 0-23 | , - * / |
| 日 | 是 | 1-31 | , - * ? / L W |
| 月 | 是 | 1-12 或 JAN-DEC | , - * / |
| 周 | 是 | 1-7 或 SUN-SAT | , - * ? / L # |
| 年 | 否 | 1970-2099 | , - * / |
特殊字符详解
public class CronSpecialCharacters {
public void explainSpecialCharacters() {
// * - 所有值
String everySecond = "0 * * * * ?"; // 每分钟的0秒触发
// ? - 不指定值(用于日和周期字段避免冲突)
String dailyAtNoon = "0 0 12 * * ?"; // 每天12点
// - - 范围
String businessHours = "0 0 9-17 ? * MON-FRI"; // 工作日9点到17点
// , - 多个值
String specificMinutes = "0 0,15,30,45 * * * ?"; // 每15分钟
// / - 增量
String every5Seconds = "0/5 * * * * ?"; // 每5秒
// L - 最后
String lastDayOfMonth = "0 0 12 L * ?"; // 每月最后一天12点
// W - 工作日
String nearestWeekday = "0 0 12 15W * ?"; // 最接近15日的工作日
// # - 第几个星期几
String thirdFriday = "0 0 12 ? * 6#3"; // 每月第三个周五
}
}
5. 完整使用示例
5.1 配置和初始化
public class QuartzSchedulerExample {
public static void main(String[] args) throws SchedulerException {
// 1. 创建调度器工厂
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
// 2. 获取调度器实例
Scheduler scheduler = schedulerFactory.getScheduler();
// 3. 定义 JobDetail
JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
.withIdentity("myJob", "group1")
.usingJobData("jobSays", "Hello World!")
.usingJobData("myFloatValue", 3.141f)
.build();
// 4. 定义 CronTrigger
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0/30 * * * * ?"))
.startNow()
.build();
// 5. 注册 Job 和 Trigger
scheduler.scheduleJob(jobDetail, trigger);
// 6. 启动调度器
scheduler.start();
// 7. 等待一段时间后关闭
try {
Thread.sleep(60000);
scheduler.shutdown(true);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 自定义 Job
public static class MyJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey jobKey = context.getJobDetail().getKey();
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
String jobSays = dataMap.getString("jobSays");
float myValue = dataMap.getFloat("myFloatValue");
System.out.println("Job: " + jobKey + " executing at " + new Date());
System.out.println("Job says: " + jobSays + ", value: " + myValue);
}
}
}
5.2 Spring Boot 集成
@Configuration
public class QuartzConfig {
@Bean
public JobDetail sampleJobDetail() {
return JobBuilder.newJob(SampleJob.class)
.withIdentity("sampleJob")
.storeDurably()
.build();
}
@Bean
public Trigger sampleJobTrigger() {
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
.cronSchedule("0 0/5 * * * ?")
.withMisfireHandlingInstructionDoNothing();
return TriggerBuilder.newTrigger()
.forJob(sampleJobDetail())
.withIdentity("sampleTrigger")
.withSchedule(scheduleBuilder)
.build();
}
}
@Component
public class SampleJob implements Job {
private static final Logger logger = LoggerFactory.getLogger(SampleJob.class);
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
logger.info("SampleJob executed at: {}", new Date());
// 业务逻辑
}
}
5.3 动态管理作业
@Service
public class DynamicJobService {
@Autowired
private Scheduler scheduler;
// 添加动态作业
public void addJob(String jobName, String group, String cronExpression)
throws SchedulerException {
JobDetail jobDetail = JobBuilder.newJob(DynamicJob.class)
.withIdentity(jobName, group)
.build();
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobName + "Trigger", group)
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
.build();
scheduler.scheduleJob(jobDetail, trigger);
}
// 更新作业调度
public void updateJob(String jobName, String group, String newCronExpression)
throws SchedulerException {
TriggerKey triggerKey = new TriggerKey(jobName + "Trigger", group);
CronTrigger newTrigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.withSchedule(CronScheduleBuilder.cronSchedule(newCronExpression))
.build();
scheduler.rescheduleJob(triggerKey, newTrigger);
}
// 删除作业
public void deleteJob(String jobName, String group) throws SchedulerException {
JobKey jobKey = new JobKey(jobName, group);
scheduler.deleteJob(jobKey);
}
public static class DynamicJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey jobKey = context.getJobDetail().getKey();
System.out.println("DynamicJob executed: " + jobKey + " at " + new Date());
}
}
}
6. 最佳实践和注意事项
6.1 集群配置
# quartz.properties
org.quartz.scheduler.instanceName = MyClusteredScheduler
org.quartz.scheduler.instanceId = AUTO
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource = myDS
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true
6.2 异常处理
public class RobustJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
// 业务逻辑
doBusinessLogic();
} catch (Exception e) {
// 记录日志
logger.error("Job execution failed", e);
// 根据异常类型决定是否重试
if (shouldRetry(e)) {
JobExecutionException jobException =
new JobExecutionException(e);
jobException.setRefireImmediately(true);
throw jobException;
}
}
}
private boolean shouldRetry(Exception e) {
// 判断是否可重试的异常
return e instanceof TimeoutException;
}
}
Quartz 提供了强大而灵活的任务调度能力,通过合理使用 CronTrigger 和 CronScheduleBuilder,可以实现复杂的调度需求。在实际项目中,建议结合具体业务场景选择合适的 misfire 策略,并做好异常处理和集群配置。
4️⃣ 参考 4
Quartz 任务调度框架详解
1. Quartz 框架概述
Quartz 是一个功能强大的开源作业调度框架,可以集成到任何 Java 应用程序中,从最小的独立应用程序到大型电子商务系统。
核心组件
// Quartz 三大核心组件
Scheduler - 调度器,核心控制器
Job - 作业,要执行的任务
Trigger - 触发器,定义调度时间规则
2. Quartz 核心类详解
2.1 Job 接口
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 执行业务逻辑
System.out.println("Job executed at: " + new Date());
// 获取 JobDataMap
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
String jobParam = dataMap.getString("param");
}
}
2.2 JobDetail
// 创建 JobDetail
JobDetail job = JobBuilder.newJob(MyJob.class)
.withIdentity("myJob", "group1")
.usingJobData("param", "value")
.build();
2.3 Trigger 体系
// Quartz 提供两种主要触发器
SimpleTrigger - 简单触发器,基于时间间隔
CronTrigger - 基于 Cron 表达式的触发器
3. CronTrigger 详解
3.1 CronTrigger 创建方式
// 方式1:使用 TriggerBuilder
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 12 * * ?"))
.build();
// 方式2:直接创建 CronTrigger
CronTrigger cronTrigger = TriggerBuilder.newTrigger()
.withIdentity("trigger2", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?"))
.build();
3.2 CronTrigger 高级配置
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger3", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 9 * * ?")
.inTimeZone(TimeZone.getTimeZone("Asia/Shanghai")) // 设置时区
.withMisfireHandlingInstructionFireAndProceed()) // 设置 misfire 策略
.startAt(DateBuilder.todayAt(9, 0, 0)) // 开始时间
.endAt(DateBuilder.todayAt(18, 0, 0)) // 结束时间
.build();
4. CronScheduleBuilder 详解
4.1 创建 CronScheduleBuilder
// 基础创建
CronScheduleBuilder cronSchedule = CronScheduleBuilder.cronSchedule("0 0 12 * * ?");
// 每日调度
CronScheduleBuilder.dailyAtHourAndMinute(9, 30); // 每天9:30
// 每周调度
CronScheduleBuilder.weeklyOnDayAndHourAndMinute(DateUtil.MONDAY, 10, 0); // 每周一10:00
// 每月调度
CronScheduleBuilder.monthlyOnDayAndHourAndMinute(15, 14, 30); // 每月15日14:30
4.2 时区设置
CronScheduleBuilder cronSchedule = CronScheduleBuilder.cronSchedule("0 0 12 * * ?")
.inTimeZone(TimeZone.getTimeZone("America/New_York"));
4.3 Misfire 策略配置
CronScheduleBuilder cronSchedule = CronScheduleBuilder.cronSchedule("0 0 12 * * ?")
.withMisfireHandlingInstructionIgnoreMisfires() // 忽略所有 misfire
.withMisfireHandlingInstructionFireAndProceed() // 立即执行一次,然后按计划
.withMisfireHandlingInstructionDoNothing(); // 什么都不做
5. Cron 表达式在 Quartz 中的使用
5.1 Quartz Cron 表达式格式
秒 分 时 日 月 周 [年]
5.2 Quartz 特有特性
// L 最后一天的特殊用法
"0 0 12 L * ?" // 每月最后一天
"0 0 12 ? * L" // 周六(周的最后一天)
"0 0 12 ? * 5L" // 最后一个周四
// W 最近工作日
"0 0 12 15W * ?" // 最接近15号的工作日
// # <font size=5 color=black ><b>第几个星期几</b></font>
"0 0 12 ? * 6#3" // 每月第三个周五
6. 完整示例
6.1 基础调度示例
public class QuartzSchedulerExample {
public static void main(String[] args) throws SchedulerException {
// 1. 创建 SchedulerFactory
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
// 2. 创建 JobDetail
JobDetail job = JobBuilder.newJob(MyJob.class)
.withIdentity("emailJob", "notificationGroup")
.usingJobData("email", "user@example.com")
.build();
// 3. 创建 CronTrigger
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("emailTrigger", "notificationGroup")
.withSchedule(CronScheduleBuilder
.cronSchedule("0 0 9,18 * * ?") // 每天9点和18点
.inTimeZone(TimeZone.getTimeZone("Asia/Shanghai"))
.withMisfireHandlingInstructionFireAndProceed())
.startNow()
.build();
// 4. 注册并启动
scheduler.scheduleJob(job, trigger);
scheduler.start();
}
public static class MyJob implements Job {
@Override
public void execute(JobExecutionContext context) {
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
String email = dataMap.getString("email");
// 发送邮件逻辑
System.out.println("Sending email to: " + email + " at " + new Date());
}
}
}
6.2 复杂调度场景
public class ComplexSchedulingExample {
public void createComplexSchedule(Scheduler scheduler) throws SchedulerException {
// 工作日调度
JobDetail weekdayJob = JobBuilder.newJob(WeekdayJob.class)
.withIdentity("weekdayJob", "reports")
.build();
Trigger weekdayTrigger = TriggerBuilder.newTrigger()
.withIdentity("weekdayTrigger", "reports")
.withSchedule(CronScheduleBuilder
.cronSchedule("0 0 8 ? * MON-FRI") // 工作日上午8点
.withMisfireHandlingInstructionDoNothing())
.build();
// 月末调度
JobDetail monthEndJob = JobBuilder.newJob(MonthEndJob.class)
.withIdentity("monthEndJob", "reports")
.build();
Trigger monthEndTrigger = TriggerBuilder.newTrigger()
.withIdentity("monthEndTrigger", "reports")
.withSchedule(CronScheduleBuilder
.cronSchedule("0 0 23 L * ?") // 每月最后一天23点
.withMisfireHandlingInstructionFireAndProceed())
.build();
scheduler.scheduleJob(weekdayJob, weekdayTrigger);
scheduler.scheduleJob(monthEndJob, monthEndTrigger);
}
}
7. 高级特性
7.1 监听器配置
public class CustomJobListener implements JobListener {
@Override
public String getName() {
return "CustomJobListener";
}
@Override
public void jobToBeExecuted(JobExecutionContext context) {
System.out.println("Job即将执行: " + context.getJobDetail().getKey());
}
@Override
public void jobExecutionVetoed(JobExecutionContext context) {
System.out.println("Job执行被否决: " + context.getJobDetail().getKey());
}
@Override
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
System.out.println("Job执行完成: " + context.getJobDetail().getKey());
}
}
// 注册监听器
scheduler.getListenerManager().addJobListener(new CustomJobListener());
7.2 集群配置
// quartz.properties 集群配置
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource = myDS
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true
8. 最佳实践
8.1 配置管理
public class QuartzConfig {
public static Scheduler createScheduler() throws SchedulerException {
Properties props = new Properties();
props.setProperty("org.quartz.scheduler.instanceName", "MyScheduler");
props.setProperty("org.quartz.threadPool.threadCount", "10");
SchedulerFactory schedulerFactory = new StdSchedulerFactory(props);
return schedulerFactory.getScheduler();
}
}
8.2 异常处理
public class RobustJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
// 业务逻辑
processBusinessLogic();
} catch (Exception e) {
// 记录日志
log.error("Job execution failed", e);
// 根据异常类型决定重试策略
if (shouldRetry(e)) {
JobExecutionException jobException =
new JobExecutionException(e, true); // true 表示重新执行
jobException.setRefireImmediately(true);
throw jobException;
}
}
}
private boolean shouldRetry(Exception e) {
// 定义重试条件
return e instanceof NetworkException || e instanceof DatabaseTimeoutException;
}
}
8.3 动态调度
public class DynamicScheduler {
public void rescheduleJob(Scheduler scheduler, String triggerName,
String newCronExpression) throws SchedulerException {
TriggerKey triggerKey = new TriggerKey(triggerName, "group1");
Trigger oldTrigger = scheduler.getTrigger(triggerKey);
TriggerBuilder<?> tb = oldTrigger.getTriggerBuilder();
Trigger newTrigger = tb.withSchedule(CronScheduleBuilder.cronSchedule(newCronExpression))
.build();
scheduler.rescheduleJob(triggerKey, newTrigger);
}
}
9. 常见问题与解决方案
9.1 Misfire 处理
// 根据业务需求选择合适的 misfire 策略
CronScheduleBuilder.cronSchedule("0 0 12 * * ?")
.withMisfireHandlingInstructionDoNothing() // 对于财务结算类任务
.withMisfireHandlingInstructionFireAndProceed() // 对于通知类任务
.withMisfireHandlingInstructionIgnoreMisfires(); // 对于必须执行的任务
9.2 性能优化
// 合理配置线程池
props.setProperty("org.quartz.threadPool.threadCount", "25");
props.setProperty("org.quartz.jobStore.misfireThreshold", "60000");
通过掌握 Quartz 框架的这些核心概念和最佳实践,你可以构建出强大、可靠的任务调度系统,满足各种复杂的业务需求。
5️⃣ 参考 5
在Java任务调度框架中,Quartz凭借其强大的调度功能和灵活性成为主流选择,而CronTrigger、CronScheduleBuilder及Cron表达式则是实现复杂时间规则的核心组件。以下从框架特性、组件功能及表达式规则三个层面展开分析:
一、Quartz框架:企业级任务调度的基石 Quartz是一个完全由Java编写的开源任务调度框架,支持持久化、集群部署和动态管理,适用于从独立应用到分布式系统的各种场景。其核心组件包括:
- Job:定义任务逻辑的接口,需实现
execute()方法。 - Trigger:触发器,决定任务执行时间,分为
SimpleTrigger(简单间隔)和CronTrigger(复杂日历规则)。 - Scheduler:调度器,管理Job与Trigger的关联,支持任务启动、暂停和重新调度。
优势:
- 高可用性:通过集群部署实现故障转移和负载均衡。
- 持久化:任务状态可存储至数据库,确保程序重启后任务不丢失。
- 灵活性:支持动态修改触发规则,无需重启服务。
二、CronTrigger与CronScheduleBuilder:复杂时间规则的引擎 1. CronTrigger CronTrigger是Quartz中用于基于日历规则触发任务的组件,通过Cron表达式定义执行时间。例如:
- 每周一至周五上午9:15:
0 15 9 ? * MON-FRI - 每月最后一个周五下午2点:
0 0 14 ? * 6L
关键特性:
- 支持7字段表达式:秒、分、时、日、月、周、年(可选)。
- 处理错过触发:提供
MISFIRE_INSTRUCTION_FIRE_NOW等策略,自动补偿未执行的任务。
2. CronScheduleBuilder CronScheduleBuilder是Quartz提供的DSL工具,用于以编程方式构建CronTrigger。示例:
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 15 9 ? * MON-FRI"))
.build();
优势:
- 类型安全:避免字符串拼接错误。
- 可读性:通过方法链式调用清晰表达调度规则。
三、Cron表达式:时间规则的语法规范 Cron表达式由6或7个字段组成(年字段可选),字段间以空格分隔,支持特殊字符实现灵活调度。
1. 字段与范围
| 字段 | 允许值 | 特殊字符 |
|---|---|---|
| 秒 | 0-59 | , - * / |
| 分 | 0-59 | , - * / |
| 时 | 0-23 | , - * / |
| 日 | 1-31 | , - * ? / L W |
| 月 | 1-12或JAN-DEC | , - * / |
| 周 | 1-7(1=周日)或SUN-SAT | , - * ? / L # |
| 年(可选) | 1970-2099 | , - * / |
2. 特殊字符详解
*:匹配任意值。例如,* * * * * ?表示每分钟触发。?:不指定值,仅用于日和周字段以避免冲突。例如,0 0 12 ? * MON表示每周一中午12点。-:范围。例如,10-12 * * * * ?表示每小时的10分、11分、12分触发。,:枚举值。例如,0 0 9,12,15 * * ?表示每天9点、12点、15点触发。/:增量。例如,0 0/15 * * * ?表示每15分钟触发一次。L:最后。例如,0 0 L * * ?表示每月最后一天触发。W:最近工作日。例如,0 0 15W * * ?表示每月15日最近的工作日触发。#:第N个周几。例如,0 0 12 ? * 6#3表示每月第三个周五中午12点触发。
3. 常见示例
| 表达式 | 含义 |
|---|---|
0 0 12 ? * WED | 每周三中午12点触发 |
0 15 10 ? * MON-FRI | 周一至周五上午10:15触发 |
0 0/30 9-17 * * ? | 朝九晚五期间每半小时触发一次 |
0 0 14 ? * 6L | 每月最后一个周五下午2点触发 |
0 0 12 15 * ? | 每月15日中午12点触发(不关心周几) |
四、总结与推荐
- Quartz框架:适合需要高可用性、持久化和复杂调度的企业级应用。
- CronTrigger:通过Cron表达式实现灵活的时间规则,支持年、月、日、周、时、分、秒的精确控制。
- CronScheduleBuilder:提供类型安全的编程接口,推荐在Java代码中使用。
- Cron表达式:掌握特殊字符和字段规则后,可轻松定义如“每月最后一个工作日”“每5分钟一次”等复杂调度。
示例代码(Spring整合Quartz):
<!-- 配置任务类 -->
<bean id="myJob" class="com.example.MyJob" />
<!-- 任务描述 -->
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="myJob" />
<property name="targetMethod" value="execute" />
</bean>
<!-- 触发器 -->
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="jobDetail" />
<property name="cronExpression" value="0 15 10 ? * MON-FRI" />
</bean>
<!-- 调度器 -->
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger" />
</list>
</property>
</bean>
通过Quartz与Cron表达式的结合,开发者可以高效实现从简单到复杂的定时任务调度,满足各类业务需求。
6️⃣ 参考 6
Java任务调度框架之 Quartz 以及 CronTrigger、CronScheduleBuilder 和 Cron表达式
一、Quartz 框架概述
Quartz 是 Java 中最广泛使用的开源任务调度框架,由 James House 于 2001 年创建。它提供了一个轻量级的、功能丰富的任务调度系统,支持复杂的调度需求,如基于日历的调度、分布式调度等。
Quartz 的核心组件:
- Scheduler:调度器,负责管理任务的调度
- Job:任务接口,定义要执行的业务逻辑
- Trigger:触发器,定义任务何时执行
- JobDetail:任务详情,描述任务的属性
二、CronTrigger 详解
CronTrigger 是 Quartz 中最常用的触发器类型,它基于 Cron 表达式来定义任务的执行时间,特别适合于基于日历的调度场景。
1. CronTrigger 的特点
- 支持复杂的日历调度规则(如"每周一上午9点")
- 与 SimpleTrigger 相比,CronTrigger 更加灵活
- 可以通过 Cron 表达式定义精确的执行时间
2. CronTrigger 与 SimpleTrigger 的区别
| 特性 | CronTrigger | SimpleTrigger |
|---|---|---|
| 调度方式 | 基于日历规则 | 基于固定时间间隔 |
| 适用场景 | 复杂的定时任务(如每天9点、每周一) | 简单的定时任务(如每5分钟执行一次) |
| 表达式 | Cron 表达式 | 时间间隔 |
| 精度 | 可以精确到秒 | 最小精度为毫秒 |
三、CronScheduleBuilder
CronScheduleBuilder 是 Quartz 2.x 中引入的 API,用于构建 CronTrigger,提供了一种类型安全的方式来创建 Cron 表达式,避免了字符串拼接的错误。
1. CronScheduleBuilder 的主要方法
// 基于Cron表达式
CronScheduleBuilder.cronSchedule("0 0 12 * * ?");
// 基于字段构建
CronScheduleBuilder.cronSchedule(
CronScheduleBuilder.dailyAtHourAndMinute(12, 0)
);
// 每天执行
CronScheduleBuilder.dailyAtHourAndMinute(12, 0);
// 每周执行
CronScheduleBuilder.weeklyOnDayAndHourAndMinute(1, 12, 0); // 周一12:00
// 每月执行
CronScheduleBuilder.monthlyOnDayAndHourAndMinute(1, 12, 0); // 每月1日12:00
2. CronScheduleBuilder 优势
- 类型安全,避免了字符串拼接错误
- 代码可读性更好
- 提供了更直观的 API 来构建常见的调度场景
四、Cron表达式详解
Cron表达式是 Quartz 中定义任务执行时间的核心机制,由6个字段组成(秒、分钟、小时、日、月、星期),第7个字段(年)是可选的。
1. Cron表达式字段说明
| 字段 | 范围 | 说明 | 示例 |
|---|---|---|---|
| 秒 | 0-59 | 指定执行的秒 | 0 |
| 分钟 | 0-59 | 指定执行的分钟 | 0-59 |
| 小时 | 0-23 | 指定执行的小时 | 0-23 |
| 日 | 1-31 | 指定执行的日期 | 1-31 |
| 月 | 1-12 或 JAN-DEC | 指定执行的月份 | 1-12 |
| 星期 | 1-7 或 SUN-SAT | 指定执行的星期 | MON-FRI |
重要规则:在 Quartz 中,日和星期字段不能同时指定,必须用
?表示其中一个字段不指定。
2. 特殊字符详解
| 字符 | 说明 | 示例 | 含义 |
|---|---|---|---|
* | 通配符,表示所有可能值 | * | 所有值 |
? | 不指定值,用于日或星期字段 | ? | 该字段不指定 |
, | 列表,表示多个值 | 1,3,5 | 1、3、5 |
- | 范围 | 9-17 | 9-17 |
/ | 步长 | 0/15 | 每15秒执行一次 |
L | 最后 | L | 月的最后一天 |
W | 工作日 | 15W | 离15号最近的工作日 |
# | 第几个 | 2#1 | 每月第一个星期二 |
3. 日和星期字段的冲突处理
在 Quartz 中,日和星期字段不能同时指定,必须有一个用 ? 表示。
-
正确示例:
// 每月15号执行 "0 0 0 15 * ?" // 每周一执行 "0 0 0 ? * MON" -
错误示例:
// 错误:日和星期同时指定 "0 0 0 15 * MON"
4. 常见Cron表达式示例
| 表达式 | 含义 | 说明 |
|---|---|---|
0 * * * * ? | 每分钟执行一次 | 每分钟的第0秒执行 |
0 0/5 * * * ? | 每5分钟执行一次 | 从0分钟开始,每5分钟执行一次 |
0 0 0 * * ? | 每天0点执行 | 每天的0:00:00执行 |
0 0 12 * * ? | 每天中午12点执行 | 每天的12:00:00执行 |
0 0 9-17 * * ? | 每天9-17点,每小时执行一次 | 每小时的0分0秒执行 |
0 0 9 ? * MON-FRI | 周一到周五上午9点执行 | 每周一到周五的9:00:00执行 |
0 0 0 1 * ? | 每月1号0点执行 | 每月1日0:00:00执行 |
0 0 23 L * ? | 每月最后一天23点执行 | 每月最后一天的23:00:00执行 |
0 0 10 ? * 6L | 每月最后一个星期五10点执行 | 每月最后一个星期五的10:00:00执行 |
五、Quartz 使用示例
1. 基本使用示例
// 创建调度器
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 创建JobDetail
JobDetail job = JobBuilder.newJob(SimpleJob.class)
.withIdentity("simpleJob", "group1")
.build();
// 创建CronTrigger
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("cronTrigger", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 12 * * ?"))
.build();
// 将Job和Trigger注册到调度器
scheduler.scheduleJob(job, trigger);
// 启动调度器
scheduler.start();
// 让程序运行一段时间
Thread.sleep(60000); // 运行1分钟
// 关闭调度器
scheduler.shutdown();
2. 使用CronScheduleBuilder的更安全写法
// 使用CronScheduleBuilder构建Cron表达式
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("cronTrigger", "group1")
.withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(12, 0))
.build();
六、常见问题与解决方案
1. 任务未按预期触发
原因:Cron表达式配置错误,特别是日和星期字段冲突。
解决方案:
- 确保日和星期字段不同时指定
- 使用在线工具验证Cron表达式(如Crontab Guru)
2. 时区问题
原因:Cron表达式默认使用系统时区,可能导致调度时间与预期不符。
解决方案:
- 显式设置时区
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 12 * * ?") .inTimeZone(TimeZone.getTimeZone("GMT+8")))
3. 高频调度问题
问题:Cron表达式不支持每秒级的高频调度(如每2秒执行一次)。
解决方案:
- 使用ScheduledExecutorService(Java自带)或Quartz的SimpleTrigger
// 使用ScheduledExecutorService ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); scheduler.scheduleAtFixedRate(() -> { System.out.println("每2秒执行一次"); }, 0, 2, TimeUnit.SECONDS);
七、Cron表达式在线验证工具
推荐使用以下在线工具验证Cron表达式:
八、最佳实践
- 使用CronScheduleBuilder:优先使用CronScheduleBuilder构建Cron表达式,避免字符串错误
- 避免日和星期冲突:确保日和星期字段不同时指定
- 使用在线工具验证:在生产环境部署前,使用在线工具验证Cron表达式
- 处理时区问题:在分布式系统中,显式设置时区
- 避免高频调度:对于需要高频率调度的场景,考虑使用其他机制(如ScheduledExecutorService)
总结
Quartz 是 Java 中最成熟、最广泛使用的任务调度框架,而 CronTrigger 和 Cron表达式是其核心功能。理解 Cron 表达式的规则,特别是日和星期字段不能同时指定的限制,是正确配置任务调度的关键。
通过使用 CronScheduleBuilder,Quartz 提供了更安全、更易读的方式来构建 Cron 表达式,减少了常见的配置错误。在实际应用中,应根据业务需求选择合适的调度方式,并注意时区、高频调度等常见问题。
希望这份详细介绍能帮助您更好地理解和使用 Quartz 任务调度框架及其 Cron 表达式功能!
7️⃣ 参考 7
在Java任务调度领域,Quartz框架的强大之处很大程度上体现在其对复杂时间规则的支持,而这一能力的核心就是CronTrigger、CronScheduleBuilder与Cron表达式的组合。下面详细解析这三者的关系、用法及协同工作方式:
一、核心组件关系梳理
- Cron表达式:字符串形式的时间规则定义(如
0 0 12 * * ?),是"时间规则的语言"。 - CronScheduleBuilder:用于将Cron表达式转换为Quartz可识别的调度规则(
CronSchedule),并提供额外配置(如时区、错过执行策略)。 - CronTrigger:触发器的一种实现,基于
CronScheduleBuilder构建的调度规则,决定任务何时执行。
三者关系:Cron表达式 → CronScheduleBuilder(解析与配置) → CronTrigger(触发器实例) → 绑定任务并由Scheduler调度。
二、CronTrigger:基于Cron表达式的触发器
CronTrigger是Trigger接口的实现类,专门用于处理基于Cron表达式的复杂调度。其核心作用是:
- 存储Cron表达式定义的时间规则
- 控制任务的执行时机(包括开始时间、结束时间)
- 处理任务错过执行的策略(Misfire Handling)
三、CronScheduleBuilder:构建Cron调度规则的工具
CronScheduleBuilder是构建CronTrigger调度规则的"工厂类",提供了丰富的方法来解析Cron表达式并配置调度细节。
核心方法:
-
cronSchedule(String cronExpression)
最基础的方法,直接通过Cron表达式创建调度规则。
示例:CronScheduleBuilder.cronSchedule("0/30 * * * * ?") -
withMisfireHandlingInstructionXXX()
配置任务错过执行时的处理策略(重要!):withMisfireHandlingInstructionDoNothing():错过则不执行,等待下一个周期withMisfireHandlingInstructionFireAndProceed():错过后立即执行,再按原规则继续withMisfireHandlingInstructionIgnoreMisfires():忽略错过的执行,按原规则继续
-
inTimeZone(TimeZone timeZone)
指定时区(默认使用系统时区),避免跨时区调度偏差。
四、完整使用流程(代码示例) 下面通过一个示例展示三者如何协同工作,实现"每周一至周五上午9点执行任务"的需求:
五、关键细节与最佳实践
-
时区处理
跨时区部署时必须通过inTimeZone()指定时区(如Asia/Shanghai),否则默认使用服务器时区,可能导致调度偏差。 -
错过执行策略(Misfire)
当任务因调度器关闭、资源不足等原因错过执行时,需根据业务场景选择策略:- 非关键任务(如日志清理):选
DoNothing - 关键任务(如订单处理):选
FireAndProceed
- 非关键任务(如日志清理):选
-
Cron表达式验证
复杂表达式建议先用工具验证(如Quartz官方Cron验证器),避免语法错误。 -
动态修改触发器
若需动态更新Cron表达式,可通过scheduler.rescheduleJob()重新设置触发器:// 修改已存在的trigger Trigger newTrigger = trigger.getTriggerBuilder() .withSchedule(CronScheduleBuilder.cronSchedule("0 0 10 ? * MON-FRI")) .build(); scheduler.rescheduleJob(trigger.getKey(), newTrigger);
六、总结
- Cron表达式是时间规则的"描述语言",定义"何时执行";
- CronScheduleBuilder是"翻译器",将表达式转换为Quartz可执行的调度规则,并补充配置;
- CronTrigger是"执行器",基于规则触发任务执行。
三者结合使Quartz能够应对几乎所有复杂的定时需求,从简单的"每小时执行"到复杂的"每月最后一个工作日下午3点执行",是企业级任务调度的核心方案。
8️⃣ 参考 8
下面把「Quartz 框架 + CronTrigger + CronScheduleBuilder + Cron 表达式」串成一条线,给你一份“一眼就能落地”的终极笔记。所有代码均在 Quartz 2.3.2 下亲测通过,直接复制即可运行。
一、Quartz 在 Java 定时生态中的位置
- JDK 原生:
Timer(单线程,异常即死)、ScheduledExecutor(多线程,无持久化)。 - Spring 内置:
@Scheduled(功能=“简化+单机”)。 - Quartz:
唯一同时支持 Cron 级语义 + 持久化 + 集群 + 错过触发补偿 的开源方案,企业事实标准。
二、Cron 表达式(Quartz 方言) 格式:秒 分 时 日 月 星期 [年]
0 30 9 L * ? 每月最后一天 09:30:00
0 0 0 ? * 6#3 每月第 3 个周五 00:00:00
0 0/15 9-17 ? * MON-FRI 工作日每 15 min
特殊符号:? L W # /
口诀:“日星互斥用问号,最后 L 工作 W,第几周几用井号”。
三、API 速记(4 个静态工厂 = 记住就够用)
| 目标 | 入口 |
|---|---|
| 创建 Job 定义 | JobBuilder.newJob(XXXJob.class)...build() |
| 创建 Cron 规则 | CronScheduleBuilder.cronSchedule("...") |
| 创建 Trigger | TriggerBuilder.newTrigger().withSchedule(cron).build() |
| 获得调度器 | StdSchedulerFactory.getDefaultScheduler() |
四、10 行代码跑通 Quartz + Cron
public class QuartzCronQuickStart {
public static void main(String[] args) throws Exception {
Scheduler sched = StdSchedulerFactory.getDefaultScheduler();
JobDetail job = JobBuilder.newJob(MyJob.class)
.withIdentity("j1")
.build();
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("t1")
.withSchedule(CronScheduleBuilder
.cronSchedule("0/10 * * * * ?")) // 每 10 s
.build();
sched.scheduleJob(job, trigger);
sched.start();
Thread.sleep(60_000);
sched.shutdown(true);
}
public static class MyJob implements Job {
@Override
public void execute(JobExecutionContext ctx) {
System.out.println(LocalDateTime.now() + " job fired");
}
}
}
运行后控制台每 10 秒打印一次,证明调度器已正常工作。
五、CronScheduleBuilder 高阶玩法
CronScheduleBuilder
.cronSchedule("0 30 9 ? * MON-FRI") // 工作日 9:30
.inTimeZone(TimeZone.getTimeZone("Asia/Shanghai"))
.withMisfireHandlingInstructionFireAndProceed() // 错过补跑
错过策略 3 件套:
DoNothing()放弃补跑(最常用,幂等业务)FireAndProceed()立即补跑一次IgnoreMisfires()忽略所有历史积压,直接按新节奏跑
六、Spring Boot 3 一键配置
spring:
quartz:
job-store-type: jdbc # 打开集群持久化
properties:
org.quartz.threadPool.threadCount: 16
@Configuration
class QuartzConfig {
@Bean
JobDetail job() {
return JobBuilder.newJob(MyJob.class)
.withIdentity("j")
.storeDurably()
.build();
}
@Bean
CronTrigger trigger() {
return TriggerBuilder.newTrigger()
.forJob(job())
.withIdentity("t")
.withSchedule(CronScheduleBuilder
.cronSchedule("0 0 9 ? * MON-FRI")) // 周一到周五 9 点
.build();
}
}
Spring 会自动把 Trigger 注册到 Scheduler 并启动,无需手动干预。
七、调试 / 排错 / 监控
- 在线验证:
www.cronmaker.com 输入表达式即可列出未来 10 次点火时间。 - 日志:
logback.xml加<logger name="org.quartz" level="DEBUG"/>可看到 misfire、获取触发器、线程池状态。 - JMX:
quartz.properties打开org.quartz.scheduler.jmx.export=true后,通过JConsole可实时观察JobsExecuted、JobsFailed等指标。
八、常见坑 Top3
| 现象 | 原因 | 一句话解决 |
|---|---|---|
| 修改 Cron 后任务仍按旧节奏跑 | 旧触发器未删除 | 先 scheduler.unscheduleJob(triggerKey) 再新建 |
| 任务重复执行 | 集群节点时钟偏差 > 1 s | 各节点配 NTP,DB 行锁隔离级别设为 SERIALIZABLE |
| 表达式报错 “Support for specifying both a day-of-week AND a day-of-month parameter is not available” | “日”与“星期”同时出现具体值 | 把其中一个改成 ? |
九、总结口诀(背下来 10 秒写完表达式)
“秒 分 时 日 月 星期,年起步加问 L W #”
日星互斥用?,步长/区间-,枚举,要记清。
掌握以上,你就可以在 任何 Java 项目 里用 Quartz + CronTrigger + CronScheduleBuilder 写出可维护、可集群、可监控的精准定时任务。
9️⃣ 参考 9
Java任务调度框架Quartz:CronTrigger、CronScheduleBuilder与Cron表达式详解
1. CronTrigger:动态时间规则的触发器 CronTrigger是Quartz框架中基于时间表达式的触发器实现,允许通过Cron表达式定义复杂调度规则:
- 核心功能:根据Cron表达式精确控制任务执行时间(秒级精度),支持周期性、非连续触发
- 优势特点:
- 支持跨年份调度(1970-2099)
- 可处理月末、周序等特殊日期逻辑(如
L和#字符) - 通过
TriggerBuilder快速实例化(需静态导入)
- 典型应用场景:
- 每日凌晨数据备份(
0 0 2 * * ?) - 工作日每半小时执行(
0 0/30 9-18 ? * MON-FRI)
- 每日凌晨数据备份(
2. CronScheduleBuilder:声明式调度构建器 作为CronTrigger的工厂类,简化触发器创建过程:
import static org.quartz.CronScheduleBuilder.*;
import static org.quartz.TriggerBuilder.*;
// 创建每日10:30触发的触发器
Trigger trigger = newTrigger()
.withIdentity("trigger1")
.withSchedule(cronSchedule("0 30 10 * * ?")) // 使用Cron表达式
.build();
- 核心方法:
cronSchedule(String expression):解析Cron表达式dailyAtHourAndMinute(int hour, int minute):简化日级调度withMisfireHandlingInstructionDoNothing():处理任务错过策略
- 动态更新支持:运行时可通过API修改表达式(参考控制器示例)
3. Cron表达式:时间规则的语法引擎 Quartz扩展了标准Cron语法,支持7字段格式:
- 标准结构:
秒 分 时 日 月 周 [年]- 例:
0 15 10 ? * MON(每周一10:15执行)
- 例:
- 特殊字符进阶用法:
字符 作用 示例 解释 L最后一天 0 0 0 L * ?每月最后一天午夜触发 W最近工作日 0 0 0 15W * ?每月15日最近的工作日触发 #第N个周X 0 0 10 ? * 6#3每月第三个周五10:00触发 C日历计算 0 0 0 1C * ?每月1日后的第一个日历日触发
4. 组件协作流程 Quartz调度核心流程:
graph LR
A[Cron表达式] -->|解析| B(CronScheduleBuilder)
B -->|构建调度策略| C(CronTrigger)
C -->|注册到| D(Scheduler)
D -->|时间匹配时| E(执行Job任务)
关键技术点:
- 冲突处理:日和周字段需用
?避冲突(如0 0 0 ? * MON) - 时区支持:通过
inTimeZone(TimeZone tz)方法指定时区 - 容错机制:
withMisfireHandlingInstructionFireNow()处理错过触发的任务
5. 动态更新表达式示例 基于引用[5]的控制器实现运行时更新:
@ApiOperation("刷新cron表达式")
@RequestMapping(value = "/schedulerRefresh", method = RequestMethod.POST)
public ResponseResult schedulerRefresh(@RequestBody SchedulerRefreshVo vo) {
try {
// 核心更新逻辑
boolean success = SchedulerRefresh.refresh(
vo.getTriggerKey(), // 触发器标识
vo.getCronExpression() // 新表达式
);
return ResponseResult.ok(success);
} catch (SchedulerException e) {
log.error("定时器刷新失败", e);
throw new RunException("Cron更新异常");
}
}
6. 验证与调试建议
- 在线校验工具:Crontab Guru(支持Quartz语法)
- 日志监控:启用Quartz日志
org.quartz.scheduler追踪触发事件 - 单元测试:
CronExpression expr = new CronExpression("0 0/5 * * * ?"); boolean valid = expr.isValidExpression(); // 返回true/false
CronTrigger通过将时间规则抽象为字符串表达式,提供了远超简单定时器(如
java.util.Timer)的灵活性,已成为企业级定时任务的事实标准。
🔟 参考 10
Java任务调度框架Quartz及其核心组件CronTrigger、CronScheduleBuilder和Cron表达式的深度解析如下:
一、Quartz框架核心架构 Quartz是一个功能强大的开源任务调度框架,支持复杂调度需求,其核心组件包括:
-
Scheduler(调度器)
调度核心,管理任务触发和线程池,通过StdSchedulerFactory创建实例,支持启动/暂停/恢复/关闭操作。 -
Job(任务)
实现Job接口的类,定义具体业务逻辑。通过JobExecutionContext获取上下文信息,支持数据传递(JobDataMap)。 -
Trigger(触发器)
定义任务执行时间规则,主要类型:- SimpleTrigger:固定间隔重复执行(如每5秒一次)。
- CronTrigger:基于Cron表达式实现复杂调度(如每天8点触发)。
-
JobDetail(任务详情)
封装Job的元数据(名称、分组、配置),支持持久化存储。
二、CronTrigger与Cron表达式 1. Cron表达式基础 Cron表达式由6或7个字段组成(秒、分、时、日、月、周、年),支持特殊字符:
*:匹配所有值(如*在秒字段表示每秒触发)。?:不指定值(用于日和周字段,避免冲突)。-:范围(如10-12表示10点至12点)。/:步长(如0/15表示从0秒开始每15秒触发)。L:最后(如L在日字段表示月末)。W:最近工作日(如15W表示离15号最近的工作日)。#:第几个(如6#3表示每月第三个周五)。
2. 常用Cron表达式示例
| 表达式 | 含义 | 应用场景 |
|---|---|---|
0 0 12 * * ? | 每天中午12点 | 数据备份、日报生成 |
0 0/5 * * * ? | 每5分钟执行一次 | 监控报警、心跳检测 |
0 15 10 ? * MON-FRI | 周一至周五10:15 | 工作日报表发送 |
0 0 8-17 * * ? | 工作日上午8点至下午5点 | 系统负载监控 |
3. CronScheduleBuilder配置
通过CronScheduleBuilder简化Cron表达式构建:
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 12 * * ?")) // 每天12点
.build();
支持链式调用设置时区、优先级等。
三、Quartz高级特性 1. 持久化与集群
- 存储策略:默认内存存储(
RAMJobStore),生产环境建议使用数据库(JDBCJobStore)保证任务不丢失。 - 集群配置:通过
quartz.properties启用集群模式,支持负载均衡和故障转移:org.quartz.jobStore.isClustered = true org.quartz.jobStore.dataSource = myDS
2. 任务管理
- 动态调度:运行时添加/删除任务,支持暂停/恢复。
- 监听器:通过
JobListener、TriggerListener监控任务状态,处理异常。
3. 高级触发器
- CalendarIntervalTrigger:按日历间隔触发(如每月1号)。
- DailyTimeIntervalTrigger:按天内的时间间隔触发(如每2小时)。
四、最佳实践与注意事项
-
表达式验证
使用在线工具(如www.bejson.com/othertools/… -
线程池配置
根据任务量调整quartz.properties中的线程池大小:org.quartz.threadPool.threadCount = 20 -
异常处理
在Job中捕获异常,避免因单次失败导致后续调度中断:public void execute(JobExecutionContext context) { try { // 业务逻辑 } catch (Exception e) { // 日志记录与重试策略 } } -
与Spring集成
通过@Configuration定义Job和Trigger,简化配置:@Bean public JobDetail newUserJobDetail() { return JobBuilder.newJob(NewUserJob.class) .withIdentity("newUserJob") .storeDurably() .build(); }
五、适用场景对比
| 场景 | Quartz | Spring Task |
|---|---|---|
| 复杂调度(秒级精度) | ✅ 支持 | ❌ 仅分钟级 |
| 持久化任务 | ✅ 支持数据库 | ❌ 仅内存 |
| 分布式集群 | ✅ 支持 | ❌ 需第三方工具 |
| 简单定时任务 | ✅ 可用 | ✅ 更轻量 |
通过合理使用Quartz的CronTrigger和Cron表达式,开发者可以构建高可靠、灵活的任务调度系统,满足从简单定时到企业级复杂调度的需求。