Java的任务调度框架之Quartz 笔记250930

Java的任务调度框架之Quartz 笔记250930

一些基本用法示例

基本用法示例 1
java 复制代码
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class QuartzDemo {
    public static void main(String[] args) throws SchedulerException {
        // 1. 创建任务
        JobDetail job = JobBuilder.newJob(MyJob.class)
                .withIdentity("myJob", "group1")
                .build();

        // 2. 创建触发器,每5秒触发一次
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("myTrigger", "group1")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(5)
                        .repeatForever())
                .build();

        // 3. 创建调度器并启动
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.start();
        scheduler.scheduleJob(job, trigger);
    }

    public static class MyJob implements Job {
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println("任务执行时间: " + System.currentTimeMillis());
        }
    }
}
基本用法示例 2 使用 CRON (CronTrigger)
java 复制代码
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class QuartzCronDemo {
    public static void main(String[] args) throws SchedulerException {
        // 1. 创建任务
        JobDetail job = JobBuilder.newJob(MyJob.class)
                .withIdentity("myJob", "group1")
                .build();

        // 2. 创建 CronTrigger,每天8点执行
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("myTrigger", "group1")
                .withSchedule(CronScheduleBuilder.cronSchedule("0 0 8 * * ?"))
                .build();

        // 3. 创建调度器并启动
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.start();
        scheduler.scheduleJob(job, trigger);
    }

    public static class MyJob implements Job {
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println("定时任务执行: 每天8点");
        }
    }
}
基本用法示例 3
java 复制代码
public class SimpleQuartzDemo {
    
    // 1. 定义任务类
    public static class HelloJob implements Job {
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println("Hello Quartz! " + new Date());
        }
    }
    
    public static void main(String[] args) throws SchedulerException {
        // 2. 创建调度器
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        
        // 3. 定义任务详情
        JobDetail job = JobBuilder.newJob(HelloJob.class)
                .withIdentity("helloJob", "group1")
                .build();
        
        // 4. 定义触发器 - 立即开始,每5秒执行一次
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("helloTrigger", "group1")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(5)
                        .repeatForever())
                .build();
        
        // 5. 将任务和触发器注册到调度器
        scheduler.scheduleJob(job, trigger);
        
        // 6. 启动调度器
        scheduler.start();
        
        // 7. 运行一段时间后关闭
        try {
            Thread.sleep(30000); // 运行30秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        scheduler.shutdown(true);
    }
}
基本用法示例 4 使用 CRON (CronTrigger)
java 复制代码
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.text.SimpleDateFormat;
import java.util.Calendar;

public class SimpleCronExample {

    // 1. 定义任务:实现Job接口
    public static class MyJob implements Job {
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            // 获取任务名称
            String jobName = context.getJobDetail().getKey().getName();
            // 格式化当前时间
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
            String currentTime = dateFormat.format(Calendar.getInstance().getTime());
            // 打印任务执行信息
            System.out.println("任务Key: " + jobName + " 正在执行,执行时间: " + currentTime);
        }
    }

    public static void main(String[] args) throws Exception {
        
        // 2. 创建调度器
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        
        // 3. 创建任务详情
        JobDetail job = JobBuilder.newJob(MyJob.class)
                .withIdentity("myJob", "group1") // 设置任务名称和组
                .build();
        
        // 4. 创建Cron触发器
        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("myTrigger", "group1") // 设置触发器名称和组
                .withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?")) // 设置Cron表达式:每10秒执行一次
                .build();
        
        // 5. 将任务和触发器注册到调度器
        scheduler.scheduleJob(job, trigger);
        
        // 6. 启动调度器
        scheduler.start();
        
        // 主线程睡眠一段时间以便观察任务执行
        Thread.sleep(60000); // 运行60秒
        
        // 关闭调度器
        scheduler.shutdown(true);
    }
}
基本用法示例 5
java 复制代码
// 1. 创建Scheduler实例
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();

// 2. 定义JobDetail,关联上我们实现的Job类
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
    .withIdentity("myJob", "group1") // 设置名称和组
    .usingJobData("key", "value") // 可选:通过JobDataMap传递参数
    .build();

// 3. 定义Trigger,指定调度规则(例如:立即开始,每10秒执行一次,重复直到永远)
Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("myTrigger", "group1")
    .startNow()
    .withSchedule(SimpleScheduleBuilder.simpleSchedule()
        .withIntervalInSeconds(10)
        .repeatForever())
    .build();

// 4. 将JobDetail和Trigger注册给Scheduler,并启动调度器
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();

// 5. 实现Job接口
public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // 从context中可以获取JobDetail中设置的JobDataMap等信息
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();
        String value = dataMap.getString("key");
        System.out.println("Hello, Quartz! " + value);
    }
}
基本用法示例 6 使用 CRON (CronTrigger)
java 复制代码
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class SimpleCronExample {
    public static void main(String[] args) {
        try {
            // 1. 创建调度器
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
            scheduler.start(); // 启动调度器

            // 2. 定义一个Job,绑定我们实现的逻辑类
            JobDetail job = JobBuilder.newJob(MySimpleJob.class)
                    .withIdentity("mySimpleJob", "group1")
                    .build();

            // 3. 定义触发器,并使用Cron表达式指定执行规则(此处为每5秒一次)
            Trigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity("mySimpleTrigger", "group1")
                    .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))
                    .build();

            // 4. 将任务和触发器告知调度器
            scheduler.scheduleJob(job, trigger);

            // 让程序运行一段时间,例如60秒后停止
            Thread.sleep(60000);
            
            // 关闭调度器
            scheduler.shutdown();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 5. 实现Job接口,定义你的任务逻辑
    public static class MySimpleJob implements Job {
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println("简易任务执行! 时间: " + new java.util.Date());
        }
    }
}
基本用法示例 7
java 复制代码
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class QuartzExample {
    public static void main(String[] args) throws SchedulerException {
        // 1. 创建JobDetail,绑定自定义Job
        JobDetail job = JobBuilder.newJob(MyJob.class)
                .withIdentity("myJob", "group1")
                .withDescription("我的定时任务")
                .build();
        
        // 2. 创建触发器,每5秒执行一次
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("myTrigger", "group1")
                .startNow() // 立即开始
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(5)
                        .repeatForever()) // 无限循环
                .build();
        
        // 3. 创建调度器并关联Job和Trigger
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.scheduleJob(job, trigger);
        
        // 4. 启动调度器
        scheduler.start();
    }
    
    // 自定义Job
    public static class MyJob implements Job {
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println("任务执行:" + new java.util.Date());
        }
    }
}
基本用法示例 8 使用 CRON (CronTrigger)
java 复制代码
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class QuartzCronExample {
    public static void main(String[] args) throws SchedulerException {
        // 1. 创建一个JobDetail,绑定我们的任务类
        JobDetail job = Job = JobBuilder.newJob(MyCronJob.class)
                .withIdentity("cronJob", "cronGroup")
                .withDescription("使用Cron表达式的任务")
                .build();

        // 2. 创建Cron触发器,定义执行规则
        // 这里的Cron表达式表示:每30秒执行一次
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("cronTrigger", "cronGroup")
                .withSchedule(CronScheduleBuilder.cronSchedule("0/30 * * * * ?"))
                .startNow() // 立即开始执行
                .build();

        // 3. 创建调度器并关联任务和触发器
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.scheduleJob(job, trigger);

        // 4. 启动调度器
        scheduler.start();
        System.out.println("调度器已启动,每30秒执行一次任务...");
    }

    // 自定义任务类
    public static class MyCronJob implements Job {
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            // 任务执行的具体逻辑
            System.out.println("Cron任务执行: " + new java.util.Date());
        }
    }
}




Quartz作业调度框架详解

1. Quartz概述与核心价值

Quartz是一个功能全面、开源的任务调度服务,可以集成到从小的单片机系统到大型的电子商务系统等各种Java应用程序中。Quartz的核心价值在于它能够让你以非常精确的时间规划来执行任务,无论是简单的固定间隔任务(如每30秒执行一次),还是复杂的基于日历的调度(如"每月最后一个工作日的上午10点"),都能轻松应对。

在实际应用中,Quartz常用于执行诸如生成日报、数据同步、系统状态检查、自动对账、日志清理等需要定时自动完成的业务逻辑。

2. 核心概念解析

理解Quartz的以下几个核心概念是掌握其使用的关键:

2.1 核心组件

组件 说明 关键点
Job 定义了要执行的任务内容 只需实现void execute(JobExecutionContext context)方法
JobDetail 为Job实例提供属性设置、标识信息 承载Job的静态信息,Quartz每次执行Job时都会重新创建一个Job实例
Trigger 定义任务执行的时间规则 控制Job的执行时机
Scheduler 任务调度的核心容器,将JobDetail和Trigger绑定在一起 负责协调Job和Trigger,是Quartz框架的核心

2.2 Job的生命周期与状态管理

Quartz每次执行Job时都会创建一个新的Job实例,这意味着Job实例是无状态的。如果需要在多次执行之间保持状态,可以使用以下两种方法:

  1. @PersistJobDataAfterExecution注解:允许在Job执行后持久化JobDataMap中的数据
  2. @DisallowConcurrentExecution注解:防止同一Job的多个实例并发执行,避免任务重叠
java 复制代码
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class StatefulJobExample implements Job {
    public void execute(JobExecutionContext context) {
        // 获取并更新状态数据
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();
        int count = dataMap.getInt("count", 0);
        dataMap.put("count", count + 1);
    }
}

3. 快速入门示例

下面通过一个完整的示例展示Quartz的基本使用:

3.1 添加Maven依赖

xml 复制代码
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.2.1</version>
</dependency>

3.2 创建Job实现类

java 复制代码
public class HelloJob implements Job {
    public void execute(JobExecutionContext context) {
        // 从JobDataMap获取参数
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();
        String name = dataMap.getString("name");
        
        System.out.println("Hello, " + name + "! 当前时间: " + new Date());
    }
}

3.3 配置并启动调度

java 复制代码
public class QuartzExample {
    public static void main(String[] args) throws SchedulerException {
        // 1. 创建JobDetail
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("myJob", "group1")
                .usingJobData("name", "Quartz")
                .build();
                
        // 2. 创建Trigger(每5秒执行一次,无限循环)
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("myTrigger", "group1")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(5)
                        .repeatForever())
                .build();
                
        // 3. 创建Scheduler并调度任务
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.scheduleJob(jobDetail, trigger);
        
        // 4. 启动调度器
        scheduler.start();
        
        // 5. 运行一段时间后停止
        try {
            Thread.sleep(30000); // 运行30秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        scheduler.shutdown(true); // true表示等待所有正在执行的任务完成
    }
}

4. Trigger类型详解

Trigger是定义任务执行时间规则的核心组件,Quartz提供了多种Trigger类型:

4.1 SimpleTrigger 适合简单的重复模式,如"每隔X时间单位执行一次,共执行Y次":

java 复制代码
Trigger trigger = TriggerBuilder.newTrigger()
        .withIdentity("simpleTrigger", "group1")
        .startAt(startTime)  // 开始时间
        .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInMinutes(5)  // 每5分钟
                .withRepeatCount(10))      // 重复10次(共执行11次)
        .build();

4.2 CronTrigger 使用Cron表达式实现复杂的时间调度,功能最为强大:

java 复制代码
// 每天上午9:15执行
Trigger trigger = TriggerBuilder.newTrigger()
        .withIdentity("cronTrigger", "group1")
        .withCronSchedule("0 15 9 * * ?")
        .build();

下面是Cron表达式的字段说明:

字段 允许值 特殊字符
0-59 , - * /
0-59 , - * /
小时 0-23 , - * /
1-31 , - * ? / L W C
1-12或JAN-DEC , - * /
星期 1-7或SUN-SAT , - * ? / L C #
年(可选) 1970-2099 , - * /

常用Cron表达式示例

  • "0 0 12 * * ?" - 每天中午12点执行
  • "0 15 10 * * ?" - 每天上午10:15执行
  • "0 0/5 14 * * ?" - 每天下午2点到2:55期间的每5分钟执行
  • "0 0 9 ? * MON-FRI" - 周一至周五上午9点执行

5. 持久化与集群配置

对于生产环境,通常需要将任务配置持久化到数据库并支持集群部署。

5.1 数据库持久化配置

  1. 创建数据库表 :从Quartz官方发布包中获取对应的SQL脚本(路径通常为/src/org/quartz/impl/jdbcjobstore/),在数据库中执行创建表。

  2. 配置quartz.properties

properties 复制代码
# 调度器配置
org.quartz.scheduler.instanceName = MyClusteredScheduler
org.quartz.scheduler.instanceId = AUTO

# 线程池配置
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 20
org.quartz.threadPool.threadPriority = 5

# 作业存储配置
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
org.quartz.jobStore.clusterCheckinInterval = 20000

# 数据源配置
org.quartz.jobStore.dataSource = myDS
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

5.2 集群优势

  • 高可用性:一个节点故障时,其他节点可以接管任务
  • 负载均衡:任务可以分散到集群中的多个节点执行
  • 状态维护:通过数据库持久化保持任务状态的一致性

6. 与Spring框架集成

在实际项目中,Quartz通常与Spring框架集成使用:

6.1 Spring配置示例

xml 复制代码
<bean id="myJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <property name="jobClass" value="com.example.MySpringJob"/>
    <property name="durability" value="true"/>
</bean>

<bean id="myTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    <property name="jobDetail" ref="myJob"/>
    <property name="cronExpression" value="0 0 6 * * ?"/>
</bean>

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="myTrigger"/>
        </list>
    </property>
    <property name="dataSource" ref="dataSource"/>
    <property name="quartzProperties">
        <props>
            <prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop>
        </props>
    </property>
</bean>

7. 最佳实践与常见问题

7.1 任务设计原则

  1. 任务幂等性:确保同一任务多次执行结果一致,防止重复执行导致问题
  2. 异常处理:在Job内部妥善处理异常,避免任务异常导致调度器停止工作
  3. 执行时间控制:避免单个任务执行时间过长,影响其他任务调度

7.2 性能优化建议

  1. 合理配置线程池大小:根据任务数量和特性调整线程数
  2. 选择合适的JobStore:小型应用可使用RAMJobStore,生产环境建议使用JDBCJobStore
  3. 简化JobDataMap:避免在JobDataMap中存储大量数据

7.3 常见问题排查

  • 任务未按预期执行:检查Cron表达式是否正确,时区设置是否合理
  • 集群环境下任务重复执行:检查数据库连接和实例ID配置
  • 内存泄漏:确保不再需要的任务被正确删除和清理

Quartz作为一个成熟稳定的任务调度框架,在企业级应用中有着广泛的使用。通过合理配置和遵循最佳实践,你可以构建出可靠、高效的任务调度系统。





Java Quartz框架全面详解

Java Quartz框架全面详解

1. Quartz框架概述

1.1 什么是Quartz? Quartz是一个功能丰富的开源作业调度库,完全由Java编写,可以集成到任何Java应用程序中。它提供了强大而灵活的任务调度能力,从小型应用到大型企业级系统都能胜任。

1.2 核心特性

  • 精确调度:支持秒级精度的任务调度
  • 持久化:可将任务配置存储到数据库
  • 集群支持:支持分布式环境下的任务调度
  • 事务支持:与J2EE容器的事务集成
  • 灵活触发:支持多种触发策略
  • 监听机制:提供完整的事件监听体系

2. 核心架构与组件

2.1 Quartz架构图

scss 复制代码
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│    Scheduler    │───▶│   JobDetail     │───▶│      Job        │
│   (调度器)      │    │  (任务详情)     │    │   (任务逻辑)    │
└─────────────────┘    └─────────────────┘    └─────────────────┘
         │                       │
         ▼                       ▼
┌─────────────────┐    ┌─────────────────┐
│    Trigger      │    │  JobDataMap     │
│   (触发器)      │    │ (任务数据映射)  │
└─────────────────┘    └─────────────────┘

2.2 核心组件详解

Scheduler(调度器) 调度器的核心接口,负责管理任务和触发器。

java 复制代码
public interface Scheduler {
    // 调度任务
    void scheduleJob(JobDetail jobDetail, Trigger trigger);
    // 启动调度器
    void start();
    // 暂停调度器
    void standby();
    // 关闭调度器
    void shutdown();
    // 删除任务
    boolean deleteJob(JobKey jobKey);
}

JobDetail(任务详情) 定义任务的静态信息,每次任务执行时都会基于JobDetail创建新的Job实例。

java 复制代码
// 创建JobDetail的典型方式
JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
    .withIdentity("job1", "group1")
    .usingJobData("jobSays", "Hello World!")
    .usingJobData("myFloatValue", 3.141f)
    .build();

Job(任务接口) 业务逻辑的实现接口。

java 复制代码
public interface Job {
    void execute(JobExecutionContext context) throws JobExecutionException;
}

// 具体实现示例
public class MyJob implements Job {
    public void execute(JobExecutionContext context) {
        JobKey key = context.getJobDetail().getKey();
        JobDataMap dataMap = context.getJobDetail().getJobDataMap();
        
        String jobSays = dataMap.getString("jobSays");
        float myFloatValue = dataMap.getFloat("myFloatValue");
        
        System.out.println("Instance " + key + " of MyJob says: " + jobSays + ", and val is: " + myFloatValue);
    }
}

Trigger(触发器) 控制任务执行的时间规则。

java 复制代码
// 触发器构建器模式
Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("trigger1", "group1")
    .startNow()
    .withSchedule(SimpleScheduleBuilder.simpleSchedule()
        .withIntervalInSeconds(40)
        .repeatForever())
    .build();

3. 触发器(Trigger)深度解析

3.1 SimpleTrigger 适用于简单的时间间隔调度。

java 复制代码
// 立即开始执行,每10秒执行一次,总共执行5次
Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("trigger3", "group1")
    .startNow()
    .withSchedule(SimpleScheduleBuilder.simpleSchedule()
        .withIntervalInSeconds(10)
        .withRepeatCount(4)) // 注意:重复4次,总共执行5次
    .build();

// 在特定时间开始,每5分钟执行一次,无限制重复
Trigger trigger2 = TriggerBuilder.newTrigger()
    .withIdentity("trigger4", "group1")
    .startAt(DateBuilder.dateOf(9, 0, 0)) // 当天9点开始
    .withSchedule(SimpleScheduleBuilder.simpleSchedule()
        .withIntervalInMinutes(5)
        .repeatForever())
    .build();

3.2 CronTrigger 基于Unix cron表达式的强大触发器。

Cron表达式格式

css 复制代码
秒 分 时 日 月 星期 [年]

常用Cron表达式示例

java 复制代码
// 每天上午9点执行
Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("trigger5", "group1")
    .withSchedule(CronScheduleBuilder.cronSchedule("0 0 9 * * ?"))
    .build();

// 周一至周五上午9:30执行
Trigger trigger6 = TriggerBuilder.newTrigger()
    .withIdentity("trigger6", "group1")
    .withSchedule(CronScheduleBuilder.cronSchedule("0 30 9 ? * MON-FRI"))
    .build();

// 每月最后一天下午5点执行
Trigger trigger7 = TriggerBuilder.newTrigger()
    .withIdentity("trigger7", "group1")
    .withSchedule(CronScheduleBuilder.cronSchedule("0 0 17 L * ?"))
    .build();

Cron表达式特殊字符

字符 含义 示例
* 所有值 *在分字段表示每分钟
? 不指定值 用于日和星期字段冲突时
- 范围 MON-FRI表示周一到周五
, 列举值 MON,WED,FRI表示周一、三、五
/ 增量 0/15在分字段表示从0分开始每15分钟
L 最后 L在日字段表示最后一天
W 工作日 15W表示最接近15日的工作日

3.3 CalendarIntervalTrigger 基于日历间隔的触发器,能正确处理时区和夏令时。

java 复制代码
// 每2周执行一次
Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("trigger8", "group1")
    .startNow()
    .withSchedule(CalendarIntervalScheduleBuilder.calendarIntervalSchedule()
        .withIntervalInWeeks(2))
    .build();

4. 任务状态管理与并发控制

4.1 有状态任务(Stateful Jobs) 使用注解控制任务状态。

java 复制代码
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class ColorJob implements Job {
    private static final String FAVORITE_COLOR = "favoriteColor";
    private static final String EXECUTION_COUNT = "executionCount";
    
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap data = context.getJobDetail().getJobDataMap();
        int count = data.getInt(EXECUTION_COUNT);
        count++;
        data.put(EXECUTION_COUNT, count);
        
        String favoriteColor = data.getString(FAVORITE_COLOR);
        System.out.println("ColorJob: " + context.getJobDetail().getKey() + 
            " executing at " + new Date() + "\n" +
            "  favorite color is " + favoriteColor + "\n" + 
            "  execution count (from job map) is " + count);
    }
}

4.2 注解说明

  • @DisallowConcurrentExecution:防止同一JobDetail的多个实例并发执行
  • @PersistJobDataAfterExecution:在Job执行后更新JobDataMap

5. 监听器(Listener)机制

5.1 JobListener 监听Job相关事件。

java 复制代码
public class MyJobListener implements JobListener {
    public String getName() {
        return "MyJobListener";
    }
    
    // Job将要执行时调用
    public void jobToBeExecuted(JobExecutionContext context) {
        System.out.println("Job " + context.getJobDetail().getKey() + " is about to be executed");
    }
    
    // Job执行被否决时调用
    public void jobExecutionVetoed(JobExecutionContext context) {
        System.out.println("Job " + context.getJobDetail().getKey() + " execution was vetoed");
    }
    
    // Job执行完成后调用
    public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
        System.out.println("Job " + context.getJobDetail().getKey() + " was executed");
        if (jobException != null) {
            System.out.println("Exception thrown by job: " + jobException.getMessage());
        }
    }
}

// 注册监听器
scheduler.getListenerManager().addJobListener(new MyJobListener());

5.2 TriggerListener 监听Trigger相关事件。

java 复制代码
public class MyTriggerListener implements TriggerListener {
    public String getName() {
        return "MyTriggerListener";
    }
    
    public void triggerFired(Trigger trigger, JobExecutionContext context) {
        System.out.println("Trigger " + trigger.getKey() + " fired");
    }
    
    public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
        // 返回true将阻止Job执行
        return false;
    }
    
    public void triggerMisfired(Trigger trigger) {
        System.out.println("Trigger " + trigger.getKey() + " misfired");
    }
    
    public void triggerComplete(Trigger trigger, JobExecutionContext context, 
            Trigger.CompletedExecutionInstruction triggerInstructionCode) {
        System.out.println("Trigger " + trigger.getKey() + " completed");
    }
}

6. 持久化配置

6.1 数据库表结构 Quartz需要以下核心表:

  • QRTZ_JOB_DETAILS - 任务详情
  • QRTZ_TRIGGERS - 触发器信息
  • QRTZ_SIMPLE_TRIGGERS - 简单触发器
  • QRTZ_CRON_TRIGGERS - Cron触发器
  • QRTZ_SCHEDULER_STATE - 调度器状态
  • QRTZ_FIRED_TRIGGERS - 正在执行的触发器

6.2 持久化配置示例

properties 复制代码
# quartz.properties
org.quartz.scheduler.instanceName = MyClusteredScheduler
org.quartz.scheduler.instanceId = AUTO

# 线程池配置
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 25
org.quartz.threadPool.threadPriority = 5

# JobStore配置
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.useProperties = false
org.quartz.jobStore.dataSource = myDS

# 集群配置
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 20000

# 数据源配置
org.quartz.dataSource.myDS.driver = com.mysql.cj.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/quartz
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = password
org.quartz.dataSource.myDS.maxConnections = 10

7. 集群配置与故障转移

7.1 集群优势

  • 高可用性:节点故障自动转移
  • 负载均衡:任务在集群中均匀分布
  • 故障恢复:失败的任务可以重新执行

7.2 集群配置要点

properties 复制代码
# 启用集群
org.quartz.jobStore.isClustered = true
# 集群节点检查间隔(毫秒)
org.quartz.jobStore.clusterCheckinInterval = 20000
# 设置实例ID为自动生成
org.quartz.scheduler.instanceId = AUTO

8. 与Spring框架集成

8.1 Spring配置示例

xml 复制代码
<!-- 配置Job -->
<bean id="myJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <property name="jobClass" value="com.example.MySpringJob"/>
    <property name="durability" value="true"/>
</bean>

<!-- 配置Trigger -->
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    <property name="jobDetail" ref="myJob"/>
    <property name="cronExpression" value="0/5 * * * * ?"/>
</bean>

<!-- 配置Scheduler -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="cronTrigger"/>
        </list>
    </property>
    <property name="dataSource" ref="dataSource"/>
    <property name="quartzProperties">
        <props>
            <prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop>
            <prop key="org.quartz.jobStore.isClustered">true</prop>
        </props>
    </property>
</bean>

8.2 使用Spring的MethodInvokingJobDetailFactoryBean

xml 复制代码
<bean id="myService" class="com.example.MyService"/>

<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="targetObject" ref="myService"/>
    <property name="targetMethod" value="doTask"/>
    <property name="concurrent" value="false"/>
</bean>

9. 高级特性与最佳实践

9.1 错过触发处理(Misfire Instructions) 当触发器错过触发时间时的处理策略。

java 复制代码
// SimpleTrigger的错过触发策略
Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("trigger9", "group1")
    .withSchedule(SimpleScheduleBuilder.simpleSchedule()
        .withIntervalInMinutes(5)
        .repeatForever()
        .withMisfireHandlingInstructionNextWithExistingCount())
    .build();

// CronTrigger的错过触发策略
Trigger trigger10 = TriggerBuilder.newTrigger()
    .withIdentity("trigger10", "group1")
    .withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?")
        .withMisfireHandlingInstructionDoNothing())
    .build();

9.2 最佳实践

  1. 任务设计原则

    • 保持任务简短且幂等
    • 合理处理异常
    • 避免在JobDataMap中存储大对象
  2. 性能优化

    • 合理设置线程池大小
    • 选择合适的持久化策略
    • 定期清理已完成的任务
  3. 监控与管理

    • 实现监控接口跟踪任务执行
    • 使用JMX进行运行时管理
    • 记录详细的任务执行日志

9.3 常见问题排查

java 复制代码
// 获取调度器统计信息
SchedulerMetaData metaData = scheduler.getMetaData();
System.out.println("Executed Jobs: " + metaData.getNumberOfJobsExecuted());
System.out.println("Running Since: " + metaData.getRunningSince());

// 检查任务状态
List<JobExecutionContext> currentlyExecutingJobs = scheduler.getCurrentlyExecutingJobs();
for (JobExecutionContext jobCtx : currentlyExecutingJobs) {
    System.out.println("Currently executing: " + jobCtx.getJobDetail().getKey());
}

Quartz作为一个成熟的企业级任务调度框架,通过合理的配置和使用,可以构建出稳定可靠的定时任务系统。掌握其核心概念和高级特性,能够帮助开发者在实际项目中更好地运用这一强大工具。





Quartz框架用法示例

Quartz框架用法完整示例

1. 基础环境搭建

1.1 Maven依赖配置

xml 复制代码
<dependencies>
    <!-- Quartz核心依赖 -->
    <dependency>
        <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz</artifactId>
        <version>2.3.2</version>
    </dependency>
    
    <!-- Quartz依赖的slf4j -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.30</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.7.30</version>
    </dependency>
    
    <!-- 如果需要数据库持久化 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.23</version>
    </dependency>
</dependencies>

2. 基础用法示例

2.1 最简单的任务调度

java 复制代码
public class SimpleQuartzDemo {
    
    // 1. 定义任务类
    public static class HelloJob implements Job {
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            System.out.println("Hello Quartz! " + new Date());
        }
    }
    
    public static void main(String[] args) throws SchedulerException {
        // 2. 创建调度器
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        
        // 3. 定义任务详情
        JobDetail job = JobBuilder.newJob(HelloJob.class)
                .withIdentity("helloJob", "group1")
                .build();
        
        // 4. 定义触发器 - 立即开始,每5秒执行一次
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("helloTrigger", "group1")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(5)
                        .repeatForever())
                .build();
        
        // 5. 将任务和触发器注册到调度器
        scheduler.scheduleJob(job, trigger);
        
        // 6. 启动调度器
        scheduler.start();
        
        // 7. 运行一段时间后关闭
        try {
            Thread.sleep(30000); // 运行30秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        scheduler.shutdown(true);
    }
}

2.2 带参数传递的任务

java 复制代码
public class ParameterQuartzDemo {
    
    public static class DataProcessJob implements Job {
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            // 从JobDetail获取参数
            JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
            String fileName = jobDataMap.getString("fileName");
            int retryCount = jobDataMap.getInt("retryCount");
            
            // 从Trigger获取参数
            JobDataMap triggerDataMap = context.getTrigger().getJobDataMap();
            String triggerName = triggerDataMap.getString("triggerName");
            
            System.out.printf("处理文件: %s, 重试次数: %d, 触发器: %s, 时间: %s%n",
                    fileName, retryCount, triggerName, new Date());
            
            // 更新参数(需要在Job类上添加@PersistJobDataAfterExecution注解)
            jobDataMap.put("retryCount", retryCount + 1);
        }
    }
    
    public static void main(String[] args) throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        
        // 创建带参数的任务
        JobDetail job = JobBuilder.newJob(DataProcessJob.class)
                .withIdentity("dataJob", "group1")
                .usingJobData("fileName", "data.txt")
                .usingJobData("retryCount", 0)
                .build();
        
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("dataTrigger", "group1")
                .usingJobData("triggerName", "每日数据处理触发器")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(10)
                        .withRepeatCount(5)) // 执行6次后停止
                .build();
        
        scheduler.scheduleJob(job, trigger);
        scheduler.start();
        
        // 等待任务执行完成
        try {
            Thread.sleep(60000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        scheduler.shutdown();
    }
}

3. 复杂调度场景示例

3.1 Cron表达式调度

java 复制代码
public class CronQuartzDemo {
    
    public static class ReportJob implements Job {
        @Override
        public void execute(JobExecutionContext context) {
            JobDataMap dataMap = context.getMergedJobDataMap();
            String reportType = dataMap.getString("reportType");
            
            System.out.printf("生成%s报表 - %s%n", reportType, new Date());
            
            // 模拟报表生成业务逻辑
            try {
                Thread.sleep(2000); // 模拟耗时操作
                System.out.printf("%s报表生成完成%n", reportType);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        
        // 每日报表任务 - 每天9点执行
        JobDetail dailyJob = JobBuilder.newJob(ReportJob.class)
                .withIdentity("dailyReport", "reports")
                .usingJobData("reportType", "每日")
                .build();
        
        Trigger dailyTrigger = TriggerBuilder.newTrigger()
                .withIdentity("dailyTrigger", "reports")
                .withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(9, 0)) // 每天9点
                .build();
        
        // 周报表任务 - 每周一10点执行
        JobDetail weeklyJob = JobBuilder.newJob(ReportJob.class)
                .withIdentity("weeklyReport", "reports")
                .usingJobData("reportType", "每周")
                .build();
        
        Trigger weeklyTrigger = TriggerBuilder.newTrigger()
                .withIdentity("weeklyTrigger", "reports")
                .withSchedule(CronScheduleBuilder.weeklyOnDayAndHourAndMinute(
                        DateBuilder.MONDAY, 10, 0)) // 每周一10点
                .build();
        
        // 月报表任务 - 每月1号8点执行
        JobDetail monthlyJob = JobBuilder.newJob(ReportJob.class)
                .withIdentity("monthlyReport", "reports")
                .usingJobData("reportType", "每月")
                .build();
        
        Trigger monthlyTrigger = TriggerBuilder.newTrigger()
                .withIdentity("monthlyTrigger", "reports")
                .withSchedule(CronScheduleBuilder.monthlyOnDayAndHourAndMinute(
                        1, 8, 0)) // 每月1号8点
                .build();
        
        // 注册所有任务
        scheduler.scheduleJob(dailyJob, dailyTrigger);
        scheduler.scheduleJob(weeklyJob, weeklyTrigger);
        scheduler.scheduleJob(monthlyJob, monthlyTrigger);
        
        scheduler.start();
        
        // 显示即将执行的任务
        System.out.println("已注册的触发器:");
        for (String groupName : scheduler.getTriggerGroupNames()) {
            for (TriggerKey triggerKey : scheduler.getTriggerKeys(GroupMatcher.groupEquals(groupName))) {
                Trigger trigger = scheduler.getTrigger(triggerKey);
                Date nextFireTime = trigger.getNextFireTime();
                System.out.printf("触发器: %s, 下次执行时间: %s%n", 
                        triggerKey.getName(), nextFireTime);
            }
        }
    }
}

3.2 有状态任务控制

java 复制代码
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class StatefulQuartzDemo {
    
    public static class OrderSyncJob implements Job {
        private static final String SYNC_COUNT = "syncCount";
        private static final String LAST_SYNC_TIME = "lastSyncTime";
        
        @Override
        public void execute(JobExecutionContext context) {
            JobDataMap dataMap = context.getJobDetail().getJobDataMap();
            
            int count = dataMap.getInt(SYNC_COUNT);
            Date lastTime = (Date) dataMap.get(LAST_SYNC_TIME);
            
            System.out.printf("第%d次同步订单数据, 上次同步时间: %s%n", 
                    count + 1, lastTime);
            
            // 模拟同步业务逻辑
            try {
                Thread.sleep(3000);
                System.out.println("订单数据同步完成");
                
                // 更新状态
                dataMap.put(SYNC_COUNT, count + 1);
                dataMap.put(LAST_SYNC_TIME, new Date());
                
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) throws SchedulerException, InterruptedException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        
        JobDetail job = JobBuilder.newJob(OrderSyncJob.class)
                .withIdentity("orderSyncJob", "syncGroup")
                .usingJobData("syncCount", 0)
                .usingJobData("lastSyncTime", new Date())
                .build();
        
        // 每10秒执行一次,但由于任务执行需要3秒,且加了@DisallowConcurrentExecution,
        // 所以实际执行间隔会是10秒(而不是7秒)
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("orderSyncTrigger", "syncGroup")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(10)
                        .repeatForever())
                .build();
        
        scheduler.scheduleJob(job, trigger);
        scheduler.start();
        
        // 运行2分钟
        Thread.sleep(120000);
        scheduler.shutdown(true);
    }
}

4. 监听器使用示例

4.1 完整的监听器实现

java 复制代码
public class ListenerQuartzDemo {
    
    public static class EmailJob implements Job {
        @Override
        public void execute(JobExecutionContext context) {
            JobDataMap dataMap = context.getMergedJobDataMap();
            String emailContent = dataMap.getString("content");
            
            System.out.printf("发送邮件: %s - %s%n", emailContent, new Date());
            
            // 模拟邮件发送
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    // 任务监听器
    public static class CustomJobListener implements JobListener {
        @Override
        public String getName() {
            return "CustomJobListener";
        }
        
        @Override
        public void jobToBeExecuted(JobExecutionContext context) {
            System.out.printf(">>> 任务 %s 即将执行%n", 
                    context.getJobDetail().getKey().getName());
        }
        
        @Override
        public void jobExecutionVetoed(JobExecutionContext context) {
            System.out.printf(">>> 任务 %s 执行被否决%n", 
                    context.getJobDetail().getKey().getName());
        }
        
        @Override
        public void jobWasExecuted(JobExecutionContext context, 
                JobExecutionException jobException) {
            System.out.printf(">>> 任务 %s 执行完成", 
                    context.getJobDetail().getKey().getName());
            if (jobException != null) {
                System.out.printf(", 异常: %s%n", jobException.getMessage());
            } else {
                System.out.println(", 执行成功");
            }
        }
    }
    
    // 触发器监听器
    public static class CustomTriggerListener implements TriggerListener {
        @Override
        public String getName() {
            return "CustomTriggerListener";
        }
        
        @Override
        public void triggerFired(Trigger trigger, JobExecutionContext context) {
            System.out.printf(">>> 触发器 %s 已触发%n", trigger.getKey().getName());
        }
        
        @Override
        public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
            // 这里可以添加业务逻辑来决定是否阻止任务执行
            // 例如:检查系统资源、业务条件等
            return false; // 返回true将阻止任务执行
        }
        
        @Override
        public void triggerMisfired(Trigger trigger) {
            System.out.printf(">>> 触发器 %s 错过触发%n", trigger.getKey().getName());
        }
        
        @Override
        public void triggerComplete(Trigger trigger, JobExecutionContext context,
                Trigger.CompletedExecutionInstruction triggerInstructionCode) {
            System.out.printf(">>> 触发器 %s 执行完成%n", trigger.getKey().getName());
        }
    }
    
    public static void main(String[] args) throws SchedulerException, InterruptedException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        
        // 注册全局监听器
        scheduler.getListenerManager().addJobListener(new CustomJobListener());
        scheduler.getListenerManager().addTriggerListener(new CustomTriggerListener());
        
        JobDetail job = JobBuilder.newJob(EmailJob.class)
                .withIdentity("emailJob", "notification")
                .usingJobData("content", "这是一封测试邮件")
                .build();
        
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("emailTrigger", "notification")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(15)
                        .withRepeatCount(3))
                .build();
        
        scheduler.scheduleJob(job, trigger);
        scheduler.start();
        
        Thread.sleep(60000);
        scheduler.shutdown(true);
    }
}

5. 数据库持久化示例

5.1 数据库配置 首先创建Quartz需要的数据库表(MySQL示例):

sql 复制代码
-- 从Quartz发行包的docs/dbTables目录获取完整的SQL脚本
-- 这里简要列出主要表
CREATE TABLE QRTZ_JOB_DETAILS(
  SCHED_NAME VARCHAR(120) NOT NULL,
  JOB_NAME VARCHAR(190) NOT NULL,
  JOB_GROUP VARCHAR(190) NOT NULL,
  DESCRIPTION VARCHAR(250) NULL,
  JOB_CLASS_NAME VARCHAR(250) NOT NULL,
  IS_DURABLE VARCHAR(1) NOT NULL,
  IS_NONCONCURRENT VARCHAR(1) NOT NULL,
  IS_UPDATE_DATA VARCHAR(1) NOT NULL,
  REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
  JOB_DATA BLOB NULL,
  PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);

-- 其他表:QRTZ_TRIGGERS, QRTZ_SIMPLE_TRIGGERS, QRTZ_CRON_TRIGGERS等

5.2 持久化配置示例

java 复制代码
public class PersistentQuartzDemo {
    
    public static class BackupJob implements Job {
        @Override
        public void execute(JobExecutionContext context) {
            System.out.println("执行数据库备份任务: " + new Date());
            // 模拟备份操作
            try {
                Thread.sleep(5000);
                System.out.println("备份完成");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) throws SchedulerException {
        // 创建配置工厂
        StdSchedulerFactory factory = new StdSchedulerFactory();
        
        // 配置属性
        Properties props = new Properties();
        props.put("org.quartz.scheduler.instanceName", "MyPersistentScheduler");
        props.put("org.quartz.scheduler.instanceId", "AUTO");
        
        // 线程池配置
        props.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
        props.put("org.quartz.threadPool.threadCount", "10");
        props.put("org.quartz.threadPool.threadPriority", "5");
        
        // JobStore配置 - 使用JDBC存储
        props.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
        props.put("org.quartz.jobStore.driverDelegateClass", 
                 "org.quartz.impl.jdbcjobstore.StdJDBCDelegate");
        props.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
        props.put("org.quartz.jobStore.dataSource", "myDS");
        
        // 数据源配置
        props.put("org.quartz.dataSource.myDS.driver", "com.mysql.cj.jdbc.Driver");
        props.put("org.quartz.dataSource.myDS.URL", 
                 "jdbc:mysql://localhost:3306/quartz_demo?useSSL=false");
        props.put("org.quartz.dataSource.myDS.user", "root");
        props.put("org.quartz.dataSource.myDS.password", "password");
        props.put("org.quartz.dataSource.myDS.maxConnections", "10");
        
        try {
            factory.initialize(props);
            Scheduler scheduler = factory.getScheduler();
            
            // 创建持久化任务
            JobDetail job = JobBuilder.newJob(BackupJob.class)
                    .withIdentity("backupJob", "system")
                    .storeDurably() // 设置任务为持久化
                    .build();
            
            Trigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity("backupTrigger", "system")
                    .startNow()
                    .withSchedule(CronScheduleBuilder.cronSchedule("0 0/30 * * * ?")) // 每30分钟
                    .build();
            
            scheduler.scheduleJob(job, trigger);
            scheduler.start();
            
            System.out.println("持久化任务已启动,任务信息已保存到数据库");
            System.out.println("即使程序重启,任务也会继续执行");
            
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}

6. Spring集成示例

6.1 Spring配置方式

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <!-- 业务Bean -->
    <bean id="userService" class="com.example.UserService"/>
    
    <!-- 方法调用Job -->
    <bean id="userCleanupJob" 
          class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject" ref="userService"/>
        <property name="targetMethod" value="cleanupInactiveUsers"/>
        <property name="concurrent" value="false"/> <!-- 禁止并发 -->
    </bean>
    
    <!-- 触发器 -->
    <bean id="userCleanupTrigger" 
          class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="userCleanupJob"/>
        <property name="cronExpression" value="0 0 2 * * ?"/> <!-- 每天凌晨2点 -->
    </bean>
    
    <!-- 调度器 -->
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="userCleanupTrigger"/>
            </list>
        </property>
        <property name="quartzProperties">
            <props>
                <prop key="org.quartz.threadPool.threadCount">5</prop>
            </props>
        </property>
    </bean>
</beans>

6.2 注解配置方式

java 复制代码
@Configuration
public class QuartzSpringConfig {
    
    @Bean
    public UserService userService() {
        return new UserService();
    }
    
    @Bean
    public JobDetailFactoryBean userSyncJobDetail() {
        JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
        factoryBean.setJobClass(UserSyncJob.class);
        factoryBean.setDurability(true);
        factoryBean.setJobDataAsMap(new HashMap<String, Object>() {{
            put("batchSize", 100);
        }});
        return factoryBean;
    }
    
    @Bean
    public CronTriggerFactoryBean userSyncTrigger(JobDetail userSyncJobDetail) {
        CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
        factoryBean.setJobDetail(userSyncJobDetail);
        factoryBean.setCronExpression("0 0/5 * * * ?"); // 每5分钟
        return factoryBean;
    }
    
    @Bean
    public SchedulerFactoryBean scheduler(Trigger userSyncTrigger) {
        SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
        factoryBean.setTriggers(userSyncTrigger);
        
        Properties quartzProperties = new Properties();
        quartzProperties.setProperty("org.quartz.threadPool.threadCount", "3");
        factoryBean.setQuartzProperties(quartzProperties);
        
        return factoryBean;
    }
}

7. 实用工具类示例

7.1 Quartz管理工具类

java 复制代码
public class QuartzManager {
    
    private Scheduler scheduler;
    
    public QuartzManager() throws SchedulerException {
        this.scheduler = StdSchedulerFactory.getDefaultScheduler();
    }
    
    /**
     * 添加Cron任务
     */
    public void addCronJob(String jobName, String jobGroup, 
                          Class<? extends Job> jobClass, 
                          String cronExpression, 
                          Map<String, Object> jobData) throws SchedulerException {
        
        JobDetail jobDetail = JobBuilder.newJob(jobClass)
                .withIdentity(jobName, jobGroup)
                .build();
        
        if (jobData != null) {
            jobDetail.getJobDataMap().putAll(jobData);
        }
        
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(jobName + "Trigger", jobGroup)
                .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
                .build();
        
        scheduler.scheduleJob(jobDetail, trigger);
    }
    
    /**
     * 暂停任务
     */
    public void pauseJob(String jobName, String jobGroup) throws SchedulerException {
        scheduler.pauseJob(JobKey.jobKey(jobName, jobGroup));
    }
    
    /**
     * 恢复任务
     */
    public void resumeJob(String jobName, String jobGroup) throws SchedulerException {
        scheduler.resumeJob(JobKey.jobKey(jobName, jobGroup));
    }
    
    /**
     * 删除任务
     */
    public boolean deleteJob(String jobName, String jobGroup) throws SchedulerException {
        return scheduler.deleteJob(JobKey.jobKey(jobName, jobGroup));
    }
    
    /**
     * 立即执行一次任务
     */
    public void triggerJob(String jobName, String jobGroup) throws SchedulerException {
        scheduler.triggerJob(JobKey.jobKey(jobName, jobGroup));
    }
    
    /**
     * 获取所有任务
     */
    public List<String> getAllJobs() throws SchedulerException {
        List<String> jobList = new ArrayList<>();
        for (String groupName : scheduler.getJobGroupNames()) {
            for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName))) {
                jobList.add(groupName + "." + jobKey.getName());
            }
        }
        return jobList;
    }
    
    /**
     * 启动调度器
     */
    public void start() throws SchedulerException {
        if (!scheduler.isStarted()) {
            scheduler.start();
        }
    }
    
    /**
     * 关闭调度器
     */
    public void shutdown() throws SchedulerException {
        scheduler.shutdown(true);
    }
}

这些示例涵盖了Quartz框架的主要使用场景,从基础任务调度到高级特性如持久化、监听器、Spring集成等。根据实际需求选择合适的示例进行参考和修改。





CronTrigger ( Cron表达式格式 )

Quartz CronTrigger与Cron表达式全面详解

1. Cron表达式基础结构

1.1 标准格式 Quartz的Cron表达式由6个或7个字段组成(年字段可选),各字段用空格分隔:

css 复制代码
秒 分 时 日 月 星期 [年]

1.2 字段说明表

位置 字段 允许值 允许的特殊字符
1 0-59 , - * /
2 0-59 , - * /
3 0-23 , - * /
4 1-31 , - * ? / L W C
5 1-12 或 JAN-DEC , - * /
6 星期 1-7 或 SUN-SAT , - * ? / L C #
7 年 (可选) 1970-2099 , - * /

注意

  • 星期字段中:1=星期日, 2=星期一, ..., 7=星期六
  • 日和星期字段是互斥的,通常其中一个使用?

2. 特殊字符详解

2.1 基础特殊字符

字符 名称 说明 示例
* 所有值 匹配该字段所有有效值 *在分字段=每分钟
? 不指定 用于日和星期字段避免冲突 0 0 0 ? * MON
- 范围 指定值的范围 MON-FRI=周一到周五
, 列举 指定多个值 MON,WED,FRI=周一三五
/ 增量 指定起始值和间隔 0/15=从0开始每15单位

2.2 高级特殊字符

字符 名称 说明 示例
L 最后 月份或星期的最后一天 L在日字段=月底
W 工作日 最近的工作日 15W=最接近15日的工作日
# 第几个 月份中第几个星期几 MON#2=第2个星期一
C 日历 日历计算(较少使用) C在日字段=日历日后的第一天

3. 完整代码示例

3.1 各种Cron表达式示例

java 复制代码
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.util.Date;

public class CronTriggerDetailedExample {

    public static class DemoJob implements Job {
        @Override
        public void execute(JobExecutionContext context) throws JobExecutionException {
            JobDataMap dataMap = context.getMergedJobDataMap();
            String description = dataMap.getString("description");
            System.out.println("[" + new Date() + "] " + description);
        }
    }

    public static void main(String[] args) throws Exception {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.start();

        // 示例1: 基础间隔 - 每30秒执行
        scheduleJob(scheduler, "job1", "每30秒执行", "0/30 * * * * ?");
        
        // 示例2: 固定时间 - 每天12:30执行
        scheduleJob(scheduler, "job2", "每天12:30执行", "0 30 12 * * ?");
        
        // 示例3: 工作日调度 - 周一至周五9点到17点每小时执行
        scheduleJob(scheduler, "job3", "工作日9-17点每小时执行", "0 0 9-17 ? * MON-FRI");
        
        // 示例4: 复杂组合 - 每月第1天和第15天的10:15执行
        scheduleJob(scheduler, "job4", "每月1号和15号10:15执行", "0 15 10 1,15 * ?");
        
        // 示例5: 使用L(最后) - 每月最后一天23:59执行
        scheduleJob(scheduler, "job5", "每月最后一天23:59执行", "0 59 23 L * ?");
        
        // 示例6: 使用W(工作日) - 最接近15号的工作日9点执行
        scheduleJob(scheduler, "job6", "最接近15号的工作日9点执行", "0 0 9 15W * ?");
        
        // 示例7: 使用#(第几个) - 每月第2个周一8:30执行
        scheduleJob(scheduler, "job7", "每月第2个周一8:30执行", "0 30 8 ? * 2#2");
        
        // 示例8: 使用/增量 - 从5分开始每20分钟执行
        scheduleJob(scheduler, "job8", "每小时的5,25,45分执行", "0 5/20 * * * ?");
        
        // 示例9: 包含年份 - 2024年每天执行
        scheduleJob(scheduler, "job9", "2024年每天12点执行", "0 0 12 * * ? 2024");

        // 运行5分钟后关闭
        Thread.sleep(300000);
        scheduler.shutdown(true);
    }
    
    private static void scheduleJob(Scheduler scheduler, String jobName, 
                                   String description, String cronExpression) 
            throws SchedulerException {
        
        JobDetail job = JobBuilder.newJob(DemoJob.class)
                .withIdentity(jobName, "cron-group")
                .usingJobData("description", description + " - " + cronExpression)
                .build();
        
        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(jobName + "-trigger", "cron-group")
                .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
                .build();
        
        scheduler.scheduleJob(job, trigger);
        System.out.println("已调度: " + description);
    }
}

3.2 高级CronTrigger配置

java 复制代码
public class AdvancedCronTriggerExample {
    
    public static void main(String[] args) throws Exception {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.start();
        
        JobDetail job = JobBuilder.newJob(DemoJob.class)
                .withIdentity("advancedJob", "group1")
                .usingJobData("description", "高级CronTrigger示例")
                .build();
        
        // 创建带有高级配置的CronTrigger
        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("advancedTrigger", "group1")
                .withDescription("这是一个带有高级配置的触发器")
                
                // Cron表达式:工作日每15分钟执行
                .withSchedule(CronScheduleBuilder.cronSchedule("0 0/15 * ? * MON-FRI")
                    
                    // 设置时区
                    .inTimeZone(TimeZone.getTimeZone("Asia/Shanghai"))
                    
                    // 错过触发处理策略
                    .withMisfireHandlingInstructionDoNothing()
                )
                
                // 开始时间
                .startAt(DateBuilder.todayAt(9, 0, 0)) // 今天9点开始
                
                // 结束时间
                .endAt(DateBuilder.dateOf(17, 0, 0))   // 今天17点结束
                
                // 优先级(默认5)
                .withPriority(7)
                .build();
        
        scheduler.scheduleJob(job, trigger);
        
        // 获取触发器信息
        System.out.println("下次触发时间: " + trigger.getNextFireTime());
        System.out.println("最终触发时间: " + trigger.getFinalFireTime());
        System.out.println("Cron表达式: " + trigger.getCronExpression());
        
        Thread.sleep(60000);
        scheduler.shutdown(true);
    }
}

4. 实用Cron表达式参考手册

4.1 常用表达式速查

场景 Cron表达式 说明
每秒执行 * * * * * ? 每秒执行一次
每5秒 0/5 * * * * ? 从0秒开始每5秒
每30分钟 0 0/30 * * * ? 每30分钟执行
整点执行 0 0 * * * ? 每小时0分0秒
每天固定时间 0 0 12 * * ? 每天12:00执行
工作日执行 0 0 9 ? * MON-FRI 工作日9:00执行
周末执行 0 0 10 ? * SAT,SUN 周末10:00执行
每月1号 0 0 6 1 * ? 每月1号6:00执行
每月最后一天 0 0 18 L * ? 每月最后一天18:00
季度执行 0 0 8 1 1,4,7,10 ? 每季度第一天8:00

4.2 复杂业务场景

java 复制代码
public class BusinessCronExamples {
    
    // 银行对账场景:工作日17:30执行
    public static final String BANK_RECONCILIATION = "0 30 17 ? * MON-FRI";
    
    // 数据备份场景:每天2:00执行
    public static final String DAILY_BACKUP = "0 0 2 * * ?";
    
    // 周报生成:每周一9:00执行
    public static final String WEEKLY_REPORT = "0 0 9 ? * MON";
    
    // 月报生成:每月1号10:00执行
    public static final String MONTHLY_REPORT = "0 0 10 1 * ?";
    
    // 促销活动:特定时间段执行
    public static final String PROMOTION_JOB = "0 0 9,14,19 * * ?";
    
    // 监控任务:工作时间每5分钟执行
    public static final String MONITORING = "0 0/5 9-18 ? * MON-FRI";
}

5. Cron表达式验证与调试

5.1 表达式验证工具类

java 复制代码
import org.quartz.CronExpression;
import java.text.ParseException;
import java.util.Date;

public class CronValidator {
    
    /**
     * 验证Cron表达式是否有效
     */
    public static boolean isValidCronExpression(String cronExpression) {
        return CronExpression.isValidExpression(cronExpression);
    }
    
    /**
     * 获取接下来几次执行时间
     */
    public static void printNextFireTimes(String cronExpression, int count) {
        try {
            CronExpression cron = new CronExpression(cronExpression);
            Date now = new Date();
            System.out.println("Cron表达式: " + cronExpression);
            System.out.println("接下来" + count + "次执行时间:");
            
            Date nextTime = now;
            for (int i = 0; i < count; i++) {
                nextTime = cron.getNextValidTimeAfter(nextTime);
                if (nextTime == null) {
                    System.out.println("  " + (i + 1) + ": 无更多执行时间");
                    break;
                }
                System.out.println("  " + (i + 1) + ": " + nextTime);
            }
        } catch (ParseException e) {
            System.out.println("无效的Cron表达式: " + e.getMessage());
        }
    }
    
    public static void main(String[] args) {
        // 测试验证
        String[] testExpressions = {
            "0 0 12 * * ?",           // 有效
            "0 0 12 * * ? 2024",      // 有效
            "invalid-expression",      // 无效
            "60 * * * * ?",           // 无效(秒超出范围)
        };
        
        for (String expr : testExpressions) {
            System.out.println(expr + " : " + 
                (isValidCronExpression(expr) ? "有效" : "无效"));
        }
        
        // 查看执行时间
        printNextFireTimes("0 30 9 ? * MON-FRI", 5);
    }
}

6. 常见问题与解决方案

6.1 表达式调试技巧

java 复制代码
public class CronDebugging {
    
    public static void debugCronExpression(String cronExpression) {
        try {
            CronExpression cron = new CronExpression(cronExpression);
            
            System.out.println("=== Cron表达式分析 ===");
            System.out.println("原始表达式: " + cronExpression);
            System.out.println("表达式摘要: " + cron.getExpressionSummary());
            
            // 测试执行时间
            Date baseTime = new Date();
            System.out.println("基于当前时间: " + baseTime);
            
            for (int i = 1; i <= 3; i++) {
                Date nextTime = cron.getNextValidTimeAfter(baseTime);
                if (nextTime != null) {
                    System.out.println("第" + i + "次执行: " + nextTime);
                    baseTime = nextTime;
                } else {
                    System.out.println("第" + i + "次执行: 无更多执行时间");
                    break;
                }
            }
            
        } catch (ParseException e) {
            System.out.println("表达式解析错误: " + e.getMessage());
            System.out.println("请检查以下常见问题:");
            System.out.println("1. 字段数量是否正确(6或7个字段)");
            System.out.println("2. 数值范围是否合法");
            System.out.println("3. 特殊字符使用是否正确");
            System.out.println("4. 日和星期字段是否冲突");
        }
    }
    
    public static void main(String[] args) {
        debugCronExpression("0 30 9 ? * MON-FRI");
    }
}

6.2 常见错误及修正

错误表达式 问题 修正方案
0 0 12 * * 缺少秒字段 0 0 12 * * ?
* * * * * 缺少秒字段且日和星期冲突 * * * * * ?
0 60 * * * ? 分钟值超出范围 0 0/59 * * * ?
0 0 12 1 * MON 日和星期同时指定 0 0 12 1 * ?0 0 12 ? * MON
0 0 12 L * SUN L和具体值冲突 0 0 12 ? * SUN

通过这份详细的CronTrigger和Cron表达式指南,你应该能够熟练地创建各种复杂的调度任务。记住在实际项目中,合理的Cron表达式设计对于系统稳定性和性能至关重要。





Java的任务调度框架有哪些?

1. 原生Java调度方案

1.1 Timer & TimerTask

java 复制代码
// 最简单的原生调度方案
public class TimerExample {
    public static void main(String[] args) {
        Timer timer = new Timer();
        
        // 延迟1秒后执行,每隔2秒重复执行
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("TimerTask执行: " + new Date());
            }
        }, 1000, 2000);
        
        // 运行10秒后取消
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        timer.cancel();
    }
}

特点

  • JDK内置,无需额外依赖
  • 单线程执行,任务会相互阻塞
  • 功能简单,不支持cron表达式
  • 异常处理不完善

1.2 ScheduledExecutorService

java 复制代码
public class ScheduledExecutorExample {
    public static void main(String[] args) {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
        
        // 固定延迟执行
        executor.scheduleWithFixedDelay(() -> {
            System.out.println("固定延迟任务: " + new Date());
        }, 1, 2, TimeUnit.SECONDS);
        
        // 固定频率执行
        executor.scheduleAtFixedRate(() -> {
            System.out.println("固定频率任务: " + new Date());
        }, 1, 3, TimeUnit.SECONDS);
        
        // 单次执行
        executor.schedule(() -> {
            System.out.println("单次任务执行");
        }, 5, TimeUnit.SECONDS);
    }
}

特点

  • JDK 5+ 内置
  • 线程池支持,避免任务阻塞
  • 更灵活的调度控制
  • 功能相对基础

2. 企业级调度框架

2.1 Quartz(重点)

java 复制代码
// 企业级标准选择
public class QuartzAdvantages {
    // 优势:
    // 1. 功能全面:支持cron表达式、日历排除、错过触发处理等
    // 2. 持久化:支持数据库存储任务配置
    // 3. 集群:支持分布式部署和故障转移
    // 4. 事务:支持JTA事务
    // 5. 监听器:完整的任务生命周期监听
}

适用场景

  • 复杂的企业级调度需求
  • 需要持久化和集群支持
  • 高可用性要求的系统
  • 复杂的调度规则(节假日排除等)

2.2 Spring Framework内置调度

2.2.1 @Scheduled注解

java 复制代码
@Component
public class SpringScheduledExample {
    
    // 固定速率执行
    @Scheduled(fixedRate = 5000)
    public void fixedRateTask() {
        System.out.println("固定速率任务: " + new Date());
    }
    
    // 固定延迟执行
    @Scheduled(fixedDelay = 3000)
    public void fixedDelayTask() {
        System.out.println("固定延迟任务: " + new Date());
    }
    
    // Cron表达式
    @Scheduled(cron = "0 0 9 * * MON-FRI")
    public void cronTask() {
        System.out.println("工作日9点执行");
    }
    
    // 初始延迟
    @Scheduled(initialDelay = 10000, fixedRate = 5000)
    public void initialDelayTask() {
        System.out.println("10秒后开始,每5秒执行");
    }
}

2.2.2 配置类

java 复制代码
@Configuration
@EnableScheduling
public class SchedulerConfig implements SchedulingConfigurer {
    
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
    }
    
    @Bean(destroyMethod = "shutdown")
    public Executor taskExecutor() {
        return Executors.newScheduledThreadPool(10);
    }
}

特点

  • Spring生态无缝集成
  • 注解驱动,配置简单
  • 适合简单的定时任务需求
  • 不支持持久化和集群

3. 分布式调度框架

3.1 Elastic-Job

java 复制代码
// 分布式调度解决方案
public class ElasticJobExample {
    // 主要特性:
    // 1. 分布式协调
    // 2. 弹性扩容缩容
    // 3. 失效转移
    // 4. 错过任务重触发
    // 5. 支持作业分片
}

配置示例

xml 复制代码
<!-- Elastic-Job配置 -->
<job:simple id="myElasticJob" 
            class="com.example.MyElasticJob"
            registry-center-ref="regCenter" 
            cron="0/5 * * * * ?" 
            sharding-total-count="3"
            overwrite="true"/>

适用场景

  • 大规模分布式系统
  • 需要水平扩展的调度任务
  • 高可用和负载均衡需求

3.2 XXL-Job

java 复制代码
// 分布式任务调度平台
@Component
public class XxlJobExample {
    
    @XxlJob("demoJobHandler")
    public ReturnT<String> demoJobHandler(String param) throws Exception {
        // 任务逻辑
        System.out.println("XXL-Job执行: " + param);
        return ReturnT.SUCCESS;
    }
    
    @XxlJob("shardingJobHandler")
    public ReturnT<String> shardingJobHandler(String param) throws Exception {
        // 分片参数
        ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo();
        System.out.println("分片编号:" + shardingVO.getIndex() + 
                          ", 总分片数:" + shardingVO.getTotal());
        return ReturnT.SUCCESS;
    }
}

特点

  • 提供Web管理界面
  • 支持动态调整任务参数
  • 完整的执行日志和监控
  • 故障报警机制

3.3 PowerJob

java 复制代码
// 新一代分布式任务调度框架
@Slf4j
@Component
public class PowerJobExample {
    
    @PowerJobHandler("powerJobDemo")
    public ProcessResult powerJobDemo(TaskContext context) throws Exception {
        // 获取任务参数
        String jobParams = context.getJobParams();
        
        // 处理逻辑
        log.info("PowerJob执行, 参数: {}", jobParams);
        
        // 返回结果
        return new ProcessResult(true, "执行成功");
    }
}

优势

  • 支持MapReduce执行模式
  • 工作流支持
  • 强大的可视化界面
  • 容器化部署友好

4. 轻量级调度框架

4.1 JobRunr

java 复制代码
// 基于后台线程的轻量级调度
public class JobRunrExample {
    public static void main(String[] args) {
        // 配置存储
        StorageProvider storageProvider = new InMemoryStorageProvider();
        
        // 创建后台服务
        BackgroundJobServer backgroundJobServer = 
            new BackgroundJobServer(storageProvider);
        
        // 调度任务
        BackgroundJob.schedule(
            ZonedDateTime.now().plusHours(1),
            () -> System.out.println("JobRunr延迟任务")
        );
        
        // 重复任务
        BackgroundJob.scheduleRecurring(
            "recurring-job",
            Cron.daily(),
            () -> System.out.println("每日任务")
        );
    }
}

4.2 db-scheduler

java 复制代码
// 基于数据库的轻量级调度
public class DbSchedulerExample {
    public static void main(String[] args) {
        // 数据源配置
        DataSource dataSource = ...;
        
        // 创建调度器
        Scheduler scheduler = Scheduler.create(dataSource)
            .threads(5)
            .pollingInterval(Duration.ofSeconds(10))
            .build();
        
        // 定义任务
        RecurringTask recurringTask = Tasks.recurring("my-recurring-task", 
                Schedules.fixedDelay(Duration.ofMinutes(5)))
            .execute((instance, context) -> {
                System.out.println("执行任务");
            });
        
        // 注册任务
        scheduler.schedule(recurringTask);
        
        // 启动调度器
        scheduler.start();
    }
}

5. 云原生调度方案

5.1 SchedulerX(阿里云)

java 复制代码
// 阿里云调度服务
@Component
public class SchedulerXExample {
    
    @JobProcessor("MyJobHandler")
    public ProcessResult process(JobContext context) {
        try {
            // 任务逻辑
            String parameter = context.getJobParameter();
            System.out.println("处理任务: " + parameter);
            
            return new ProcessResult(true);
        } catch (Exception e) {
            return new ProcessResult(false, e.getMessage());
        }
    }
}

5.2 Kubernetes CronJob

yaml 复制代码
# K8s原生调度方案
apiVersion: batch/v1
kind: CronJob
metadata:
  name: my-scheduled-job
spec:
  schedule: "0 6 * * *"  # 每天6点执行
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: my-job
            image: my-app:latest
            command: ["java", "-jar", "app.jar"]
          restartPolicy: OnFailure

6. 框架对比与选型指南

6.1 功能对比矩阵

框架 持久化 集群 分布式 管理界面 学习曲线 适用规模
Timer 简单 小型
ScheduledExecutor 简单 小型
Spring @Scheduled 简单 中小型
Quartz 中等 中大型
Elastic-Job 较复杂 大型分布式
XXL-Job 中等 大型分布式
PowerJob 中等 云原生分布式

6.2 选型建议

场景1:简单单机应用

java 复制代码
// 推荐:Spring @Scheduled 或 ScheduledExecutorService
@Configuration
@EnableScheduling
public class SimpleSchedulerConfig {
    // 配置简单,满足基本需求
}

场景2:传统企业应用

java 复制代码
// 推荐:Quartz
public class EnterpriseQuartzSetup {
    // 功能全面,稳定可靠
    // 支持持久化和复杂调度规则
}

场景3:微服务/分布式系统

java 复制代码
// 推荐:XXL-Job 或 PowerJob
@Component
public class DistributedJobHandler {
    // 支持分布式协调,有完善的管理界面
    // 适合云原生环境
}

场景4:高并发互联网应用

java 复制代码
// 推荐:Elastic-Job
public class InternetScaleJob {
    // 支持分片和弹性扩容
    // 适合海量数据处理
}

6.3 性能考量因素

  1. 任务数量:单机几千任务以内Quartz足够,上万任务考虑分布式方案
  2. 执行频率:秒级调度选择Quartz,分钟级以上各种方案均可
  3. 任务时长:短任务适合所有框架,长任务考虑异步和状态管理
  4. 可靠性要求:高可靠性需求选择支持持久化和集群的方案

6.4 发展趋势

  1. 云原生:Kubernetes CronJob + 自定义调度器
  2. Serverless:函数计算 + 事件驱动调度
  3. 智能化:基于机器学习的动态调度优化
  4. 可视化:统一的调度任务管理平台

7. 实际项目示例

7.1 中小型项目配置

java 复制代码
@Configuration
public class SmallProjectScheduler {
    
    @Bean
    public ThreadPoolTaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(5);
        scheduler.setThreadNamePrefix("small-project-scheduler-");
        scheduler.setWaitForTasksToCompleteOnShutdown(true);
        scheduler.setAwaitTerminationSeconds(60);
        return scheduler;
    }
    
    @Bean
    public ScheduledExecutorService backupExecutor() {
        return Executors.newScheduledThreadPool(3);
    }
}

7.2 大型项目架构

java 复制代码
@Configuration
@EnableSchedulerX
public class LargeProjectScheduler {
    
    @Bean
    public SchedulerFactoryBean clusterQuartzScheduler(DataSource dataSource) {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setDataSource(dataSource);
        factory.setQuartzProperties(quartzProperties());
        factory.setSchedulerName("ClusterQuartzScheduler");
        factory.setApplicationContextSchedulerContextKey("applicationContext");
        return factory;
    }
    
    private Properties quartzProperties() {
        Properties props = new Properties();
        props.setProperty("org.quartz.scheduler.instanceName", "ClusterScheduler");
        props.setProperty("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
        props.setProperty("org.quartz.jobStore.isClustered", "true");
        return props;
    }
}

根据项目规模、团队技术栈和具体需求,选择合适的调度框架可以大大提高开发效率和系统稳定性。





相关推荐
自由的疯2 小时前
Java(32位)基于JNative的DLL函数调用方法
java·后端·架构
咖啡Beans2 小时前
SpringBoot+Ehcache使用示例
java·spring boot
自由的疯2 小时前
Java 使用Jackson进行深拷贝:优化与最佳实践
java·后端·架构
毕设源码-郭学长2 小时前
【开题答辩全过程】以 springboot+美食电子商城的设计与实现为例,包含答辩的问题和答案
java·eclipse·美食
王嘉俊9252 小时前
Kafka 和 RabbitMQ 使用:消息队列的强大工具
java·分布式·中间件·kafka·消息队列·rabbitmq·springboot
渣哥2 小时前
事务没生效还以为成功了?Spring 事务失效的雷区你中招了吗?
java
教游泳的程序员3 小时前
【JDBC】系列文章第一章,怎么在idea中连接数据库,并操作插入数据?
java·ide·mysql·intellij-idea
懒羊羊不懒@3 小时前
C语言指针进阶(进阶)
java·开发语言·面试
nlog3n3 小时前
分布式秒杀系统设计方案
java·分布式