Java的任务调度框架之 Quartz 以及 CronTrigger,CronScheduleBuilder 和 Cron表达式 笔记250930
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 {@Overridepublic 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:基础CronTriggerCronTrigger trigger1 = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1").withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?")).build();// 方式2:带时区的CronTriggerCronTrigger trigger2 = TriggerBuilder.newTrigger().withIdentity("trigger2", "group1").withSchedule(CronScheduleBuilder.cronSchedule("0 0 9 * * ?").inTimeZone(TimeZone.getTimeZone("America/New_York"))).build();// 方式3:完整配置的CronTriggerCronTrigger 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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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@DisallowConcurrentExecutionpublic static class StatefulJob implements Job {@Overridepublic 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 {@Overridepublic 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. 创建 JobDetailJobDetail job = JobBuilder.newJob(SimpleJob.class).withIdentity("dailyJob", "reportGroup").usingJobData("jobName", "每日报表生成").usingJobData("priority", 1).build();// 3. 创建 CronTriggerCronTrigger 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:00JobDetail 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:00JobDetail 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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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();// 定义 JobJobDetail 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 {@Overridepublic 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 {@Beanpublic JobDetail sampleJobDetail() {return JobBuilder.newJob(SampleJob.class).withIdentity("sampleJob").storeDurably().build();}@Beanpublic Trigger sampleJobTrigger() {return TriggerBuilder.newTrigger().forJob(sampleJobDetail()).withIdentity("sampleTrigger").withSchedule(CronScheduleBuilder.cronSchedule("0 0/10 * * * ?")).build();}@Beanpublic 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 {@Overridepublic 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. 定义 JobDetailJobDetail jobDetail = JobBuilder.newJob(MyJob.class).withIdentity("myJob", "group1").usingJobData("jobSays", "Hello World!").usingJobData("myFloatValue", 3.141f).build();// 4. 定义 CronTriggerTrigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "group1").withSchedule(CronScheduleBuilder.cronSchedule("0/30 * * * * ?")).startNow().build();// 5. 注册 Job 和 Triggerscheduler.scheduleJob(jobDetail, trigger);// 6. 启动调度器scheduler.start();// 7. 等待一段时间后关闭try {Thread.sleep(60000);scheduler.shutdown(true);} catch (InterruptedException e) {e.printStackTrace();}}// 自定义 Jobpublic static class MyJob implements Job {@Overridepublic 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 {@Beanpublic JobDetail sampleJobDetail() {return JobBuilder.newJob(SampleJob.class).withIdentity("sampleJob").storeDurably().build();}@Beanpublic 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);@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {logger.info("SampleJob executed at: {}", new Date());// 业务逻辑}
}
5.3 动态管理作业
@Service
public class DynamicJobService {@Autowiredprivate 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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {// 执行业务逻辑System.out.println("Job executed at: " + new Date());// 获取 JobDataMapJobDataMap 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. 创建 SchedulerFactorySchedulerFactory schedulerFactory = new StdSchedulerFactory();Scheduler scheduler = schedulerFactory.getScheduler();// 2. 创建 JobDetailJobDetail job = JobBuilder.newJob(MyJob.class).withIdentity("emailJob", "notificationGroup").usingJobData("email", "user@example.com").build();// 3. 创建 CronTriggerTrigger 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 {@Overridepublic 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 {@Overridepublic String getName() {return "CustomJobListener";}@Overridepublic void jobToBeExecuted(JobExecutionContext context) {System.out.println("Job即将执行: " + context.getJobDetail().getKey());}@Overridepublic void jobExecutionVetoed(JobExecutionContext context) {System.out.println("Job执行被否决: " + context.getJobDetail().getKey());}@Overridepublic 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 {@Overridepublic 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表达式:
- Crontab Guru
- Quartz 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 {@Overridepublic 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 {@BeanJobDetail job() {return JobBuilder.newJob(MyJob.class).withIdentity("j").storeDurably().build();}@BeanCronTrigger trigger() {return TriggerBuilder.newTrigger().forJob(job()).withIdentity("t").withSchedule(CronScheduleBuilder.cronSchedule("0 0 9 ? * MON-FRI")) // 周一到周五 9 点.build();}
}
Spring 会自动把 Trigger
注册到 Scheduler
并启动,无需手动干预。
七、调试 / 排错 / 监控
- 在线验证:
http://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调度核心流程:
关键技术点:
- 冲突处理:日和周字段需用
?
避冲突(如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小时)。
四、最佳实践与注意事项
-
表达式验证
使用在线工具(如https://www.bejson.com/othertools/cron/)验证复杂表达式。 -
线程池配置
根据任务量调整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表达式,开发者可以构建高可靠、灵活的任务调度系统,满足从简单定时到企业级复杂调度的需求。