Quartz定时器学习个人整理笔记(一)

Quartz定时器学习个人整理笔记(一)

帮助文档:
Quartz官网
W3Cschool–Quartz官方文档

提示:本文以应用为主,具体原理及概念可以参考如上帮助文档。


Quartz的核心概念

  • 任务Job
    job就是想要实现的任务类,每一个job必须实现org.quartz.job接口,且只需实现接口定义的execute()方法。
  • 触发器Trigger
    trigger是执行任务的触发器,比如你想每天定时3带你发送一份邮件,trigger将会设置3点进行执行该任务。trigger主要包含SimpleTrigger和CronTrigger。
  • 调度器Scheduler
    Scheduler是任务的调度器,它会将任务job及触发器trigger整合起来,负责根据trigger设定的时间来执行job。

Quartz的体系结构

在这里插入图片描述

Quartz的几个常用API

以下是Quartz编程API几个重要接口,也是Quartz的重要组件:

  • Scheduler:主程序接口,用于与调度程序交互
    Scheduler调度程序-任务执行计划表,只有安排进执行计划(通过scheduler.scheduleJob方法安排进执行计划)的任务Job,预先定义的执行时间到了的时候(任务触发trigger),该任务才会执行。
    在这里插入图片描述

  • JobBuilder:用于声明任务实例,可定义关于该任务的详情如任务名、组名等。其声明的实例将会作为一个实际执行的任务。

  • Job:希望在指定时间能被调度程序执行的任务类,可自定义。

  • JobDetail:用来定义定时任务的实例,JobDetail的实例是由JobBuilder类创建的。
    job实例在Quartz中的生命周期:每次调度器执行job时,它在调用execute方法前会创建一个新的job实例,当调用完成后,关联的job对象实例会被释放,释放的实例会被垃圾回收机制回收。
    在这里插入图片描述

  • JobDataMap:是Java Map接口的一个实现,可以容纳不限量的序列化的数据对象,在jon实例执行时,可使用其中的数据。

  • TriggerBuilder:用于创建触发器trigger实例。

  • Trigger触发器:用于触发Job,表明任务在何时执行。当调度一个Job时,需要实例一个触发器,并根据Job执行的条件调整其属性。

  • JobListener、TriggerListener、SchedulerListener监听器:用于对组件的监听。

触发器介绍

Quartz有一些不同的触发器类型,不过,用的最多的是SimpleTrigger和CronTrigger

(1) jobKey
表示job示例的标识,触发器被触发时,该指定的job实例会被执行。
(2)startTime
表示触发器的时间表,第一次开始被触发的时间,它的数据类型是java.util.Date

1.引入依赖

<dependencies>
		<!-- 工具 -->
		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz-jobs</artifactId>
			<version>2.3.0</version>
		</dependency>
		<!-- 核心包 -->
		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz</artifactId>
			<version>2.3.0</version>
		</dependency>
		<!-- log4j -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.25</version>
		</dependency>

		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>
		
	</dependencies>
	<!-- 配置maven的执行环境jdk为1.8 -->
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
				    <target>1.8</target>
				    <source>1.8</source>
				</configuration>
			</plugin>
		</plugins>
	</build>

2.SimpleTrigger

可以满足的调度需求:

  1. 在具体的时间点执行一次。
  2. 在特定的时间点执行,并以指定的间隔重复执行几次。

SimpleTrigger的属性包括:开始时间,结束时间,重复次数以及重复的间隔。这些属性的含义与您所期望的是一致的,只是关于结束时间有一些地方需要注意。

1.重复次数,可以是0,正整数,以及常量SimpleTrigger.REPEAT_INDEFINITELY。
2.重复的间隔,必须是0,或者长型的正数,表示重复。

注意:如果重复间隔为0,触发将以重复次数并发执行(或者以scheduler可以处理的近似并发数)。

(1)有状态的job和无状态的job

@PersistJobDataAfterExecution注解的使用
有状态的Job可以理解为多次job调用期间可以持有一些状态信息,这些状态信息存储在jobDataMap中,而默认的无状态job每次调用时都会创建一个新的jobDataMap,即不能对信息进行累加。

1. 无状态的job示例(任务类)
public class HelloJob implements Job {
   /* public HelloJob(){
        System.out.println("欢迎访问!");
    }
    
    Job实现类中添加setter方法对应jobDataMap的键值,Quartz框架默认的JobFactory实现类在初始化job
           实例对象时会自动地调用这些setter方法. 但是!!!如果遇到同名的key, Trigger中的.usingJobData("message","simple触发器")
           会覆盖JobDetail中的.usingJobData("message","打印日志")
    */
    private String message;
    private Integer count;//jobDetail存储的用于计数
    
    public void setCount(Integer count) {
        this.count = count;
    }
    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // 输出当前时间
        Date date=new Date();
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //获取任务类信息
        String jobName = context.getJobDetail().getJobClass().getName();
        System.out.println("任务类名称(带路径):"+context.getJobDetail().getJobClass().getName());
        System.out.println("任务类名称:"+context.getJobDetail().getJobClass().getSimpleName());
        
        //获取JobDetail任务实例的内容
        JobKey jobkey=context.getJobDetail().getKey();
     // System.out.println("工作任务名称:"+jobkey.getName()+"   ,工作任务组:"+jobkey.getGroup());
        //获取trigger的内容
        TriggerKey triggerKey = context.getTrigger().getKey();
        System.out.println("触发器名称:"+triggerKey.getName()+"   ,触发器组:"+triggerKey.getGroup());
      
     /*  //在jobDetail中获取jobDataMap存储的值
       JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
       String jobDataMessage = jobDataMap.getString("message");
       System.out.println("任务数据的参数值"+jobDataMessage);
       
       //获取触发器中jobDataMap存储的值
       String triggerMessage = context.getTrigger().getJobDataMap().getString("message");
       System.out.println("trigger数据的参数值"+triggerMessage); */
        
        /*从set方法中获取message  注意!!!如果遇到同名的key, Trigger中的.usingJobData("message","simple触发器")
        会覆盖JobDetail中的.usingJobData("message","打印日志") */
        System.out.println("从setter方法获取到的message为:"+message);
       
       //获取任务的执行时间
        System.out.println("当前任务的执行时间:"+sdf.format(context.getFireTime()));
       System.out.println("下一次任务的执行时间:"+sdf.format(context.getNextFireTime()));
       
       //累加count,将count存放到jobDataMap中   无状态的job信息
        ++count;
        context.getJobDetail().getJobDataMap().put("count", count);
        System.out.println("count:"+count);
        
    }

}

任务调度类

public class HelloSchedulerDemo {
    public static void main(String args[]) throws SchedulerException, ParseException {
        // 1.生成调度器(Scheduler)实例,从工厂中获取调度的实例(默认:实例化new StdSchedulerFactory();)
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

        // 2.生成任务实例(JobDetail),加载任务Job类,与HelloJob完成绑定,要求:helloJob必须实现Job接口
        JobDetail job1 = JobBuilder.newJob(HelloJob.class)
                .withIdentity("job1", "group1")   //  参数1:任务的名称(唯一实例);参数2:任务组的名称,多个任务可以在同一个任务组中
                .usingJobData("message","打印日志") //传map类型的值
                .usingJobData("count",0)
                .build();
//        System.out.println("任务名称"+job1.getKey().getName());
//        System.out.println("组名称"+job1.getKey().getGroup());

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        
        // 3.生成触发器实例(Trigger),立即触发,每5秒钟执行一次,直到2021-1-18 14:29:00
        SimpleTrigger trigger2 = TriggerBuilder.newTrigger().withIdentity("trigger2", "group1")
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever())
                .endAt(sdf.parse("2099-1-18 15:29:00"))
                .usingJobData("message","simple触发器") //传递参数,名称message
                .build();

        // 4.让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
        scheduler.scheduleJob(job1, trigger2);

        // 5.启动调度
        scheduler.start();
    }
}

执行结果: count并没有累加1
count没有改变

2. 有状态的job示例(在任务类上加个注解)
@PersistJobDataAfterExecution //多次调用Job期间可以持有一些状态信息,即可以实现count的累加
public class HelloJob implements Job {
  // 方法内部代码同上...
}

执行结果: count累加1

在这里插入图片描述

(2)入门案例

SimpleTrigger简单案例

1.任务类(HelloJob):

public class HelloJob implements Job {

    @Override
    public void execute(JobExecutionContext arg0) throws JobExecutionException {
        // 输出当前时间
        Date date=new Date();
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //工作内容
       System.out.println("正在执行任务...执行时间"+date.toString()+"格式化后:"+sdf.format(date));

    }

}

2.创建任务调度类(HelloSchedulerDemo):

public class HelloSchedulerDemo {
   public static void main(String args[]) throws SchedulerException{
       //1.生成调度器(Scheduler)实例,从工厂中获取调度的实例(默认:实例化new StdSchedulerFactory();)
       Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
       
       //2.生成任务实例(JobDetail),加载任务Job类,与HelloJob完成绑定,要求:helloJob必须实现Job接口
       JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
       .withIdentity("job1", "group1") //参数1:任务的名称(唯一实例); 参数2:任务组的名称,多个任务可以在同一个任务组中
       .build();
       
        System.out.println("任务名称"+job1.getKey().getName());
        //如果没有指定组名,默认是DEFAULT
        System.out.println("组名称"+job1.getKey().getGroup());
       
       //3.生成触发器实例(Trigger)
       SimpleTrigger trigger1 = TriggerBuilder.newTrigger()
       .withIdentity("trigger1", "One")//参数1:触发器的名称(唯一实例); 参数2:触发器组的名称.不必与任务组名称一致!
       .startNow() //马上启动触发器
       .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(3))//使用SimpleScheduleBuilder,每5秒钟执行一次
       .build();
       
       //4.让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
       scheduler.scheduleJob(jobDetail,trigger1 );
       
       //5.启动调度
       scheduler.start();
   }
}

2.1创建任务调度类(HelloSchedulerDemo), 实现结束时间内每5秒执行一次:

public class HelloSchedulerDemo {
   public static void main(String args[]) throws SchedulerException, ParseException{
       //1.生成调度器(Scheduler)实例,从工厂中获取调度的实例(默认:实例化new StdSchedulerFactory();)
       Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
       
       //2.生成任务实例(JobDetail),加载任务Job类,与HelloJob完成绑定,要求:helloJob必须实现Job接口
       JobDetail job1 = JobBuilder.newJob(HelloJob.class)
       .withIdentity("job1", "group1") //参数1:任务的名称(唯一实例); 参数2:任务组的名称,多个任务可以在同一个任务组中
       .build();
       
       SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
       
       //3.生成触发器实例(Trigger),立即触发,每5秒钟执行一次,直到2021-1-18 14:29:00
      SimpleTrigger trigger2=TriggerBuilder.newTrigger().withIdentity("trigger2","group1")
      .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5)
                      .repeatForever())
      .endAt(sdf.parse("2021-1-18 14:29:00"))
      .build();
       
       //4.让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
       scheduler.scheduleJob(job1,trigger2 );
       
       //5.启动调度
       scheduler.start();
   }
}

3.执行结果(部分展示):
在这里插入图片描述

3.1 执行结果(部分展示):
在这里插入图片描述
2.2

public class HelloSchedulerDemo {
    public static void main(String args[]) throws SchedulerException, ParseException {
        // 1.生成调度器(Scheduler)实例,从工厂中获取调度的实例(默认:实例化new StdSchedulerFactory();)
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

        // 2.生成任务实例(JobDetail),加载任务Job类,与HelloJob完成绑定,要求:helloJob必须实现Job接口
        JobDetail job1 = JobBuilder.newJob(HelloJob.class)
                .withIdentity("job1", "group1")   //  参数1:任务的名称(唯一实例);参数2:任务组的名称,多个任务可以在同一个任务组中
                .build();
        System.out.println("任务名称"+job1.getKey().getName());
        System.out.println("组名称"+job1.getKey().getGroup());

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        
        // 3.生成触发器实例(Trigger),立即触发,每5秒钟执行一次,直到2021-1-18 14:29:00
        SimpleTrigger trigger2 = TriggerBuilder.newTrigger().withIdentity("trigger2", "group1")
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever())
                .endAt(sdf.parse("2099-1-18 15:29:00")).build();

        // 4.让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
        scheduler.scheduleJob(job1, trigger2);

        // 5.启动调度
        scheduler.start();
    }
}
public class HelloJob implements Job {
    public HelloJob(){
        System.out.println("欢迎访问!");
    }
    
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // 输出当前时间
        Date date=new Date();
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        
        //获取JobDetail的内容
        JobKey jobkey=context.getJobDetail().getKey();
        String jobName = context.getJobDetail().getJobClass().getName();
        System.out.println("工作任务名称:"+jobkey.getName()+"   ,工作任务组:"+jobkey.getGroup());
        System.out.println("任务类名称(带路径):"+context.getJobDetail().getJobClass().getName());
        System.out.println("任务类名称:"+context.getJobDetail().getJobClass().getSimpleName());
        //工作内容
       System.out.println("正在执行任务...执行时间"+date.toString()+"格式化后:"+sdf.format(date));

    }

}

3.2执行结果(部分展示):
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210118140658285.png
2.3 指定开始时间和结束时间内,循环执行,时间间隔自定义(任务类)

public class HelloJob implements Job {
    private String message;
    private Integer count;//jobDetail存储的用于计数
    
    public void setCount(Integer count) {
        this.count = count;
    }
    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // 输出当前时间
        Date date=new Date();
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //工作内容
        System.out.println("正在执行任务...时间是:"+sdf.format(date));
        
        //获取jobKey、startTime、endTime
        Trigger trigger = context.getTrigger();
        System.out.println("jobKey名称:"+trigger.getJobKey().getName()+"  ,jobGroup名称:"+trigger.getJobKey().getGroup());
        System.out.println("任务的开始时间:"+sdf.format(trigger.getStartTime())+"  ;结束时间:"+sdf.format(trigger.getEndTime()));
    }

}

创建任务调度类:

public class HelloSchedulerDemo {
    public static void main(String args[]) throws SchedulerException, ParseException {
        // 1.生成调度器(Scheduler)实例,从工厂中获取调度的实例(默认:实例化new StdSchedulerFactory();)
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        //设置任务的开始/结束时间
        Date startDate = new Date();
        Date endDate = new Date();
        //设置任务的开始时间推迟3秒
        startDate.setTime(startDate.getTime()+3000);
        //任务的结束时间推迟10秒(即10秒后停止)
        endDate.setTime(endDate.getTime()+10000);
        
        // 2.生成任务实例(JobDetail),加载任务Job类,与HelloJob完成绑定,要求:helloJob必须实现Job接口
        JobDetail job1 = JobBuilder.newJob(HelloJob.class)
                .withIdentity("job1", "group1")   //  参数1:任务的名称(唯一实例);参数2:任务组的名称,多个任务可以在同一个任务组中
                .usingJobData("message","打印日志") //传map类型的值
                .usingJobData("count",0)
                .build();
        
        // 3.生成触发器实例(Trigger),立即触发,每5秒钟执行一次,直到2021-1-18 14:29:00
        Trigger trigger2 = TriggerBuilder.newTrigger()
                .withIdentity("trigger2", "group1")
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(1)) //循环执行,1秒执行一次
                .startAt(startDate)  //设置任务的开始时间
                .endAt(endDate)
                .build();

        // 4.让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
        scheduler.scheduleJob(job1, trigger2);

        // 5.启动调度
        scheduler.start();
    }
}

3.3执行结果
在这里插入图片描述
在这里插入图片描述
2.4 指定开始时间,循环执行时间间隔自定义,执行次数自定义(任务类同上) .withRepeatCount(3),参数为0时执行1次

创建任务调度类:

public class HelloSchedulerDemo {
    public static void main(String args[]) throws SchedulerException, ParseException {
        // 1.生成调度器(Scheduler)实例,从工厂中获取调度的实例(默认:实例化new StdSchedulerFactory();)
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        //设置任务的开始/结束时间
        Date startDate = new Date();
        //设置任务的开始时间推迟3秒
        startDate.setTime(startDate.getTime()+3000);
        
        // 2.生成任务实例(JobDetail),加载任务Job类,与HelloJob完成绑定,要求:helloJob必须实现Job接口
        JobDetail job1 = JobBuilder.newJob(HelloJob.class)
                .withIdentity("job1", "group1")   //  参数1:任务的名称(唯一实例);参数2:任务组的名称,多个任务可以在同一个任务组中
                .build();

        
        // 3.生成触发器实例(Trigger),立即触发,每5秒钟执行一次,直到2021-1-18 14:29:00
        Trigger trigger2 = TriggerBuilder.newTrigger()
                .withIdentity("trigger2", "group1")
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(1)
                        .withRepeatCount(3)) //循环执行,1秒执行一次,只会执行4次
                        //.withRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY)) //循环执行,-1意思一直循环,跟不写没区别
                .startAt(startDate)  //设置任务的开始时间
                .build();

        // 4.让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
        scheduler.scheduleJob(job1, trigger2);

        // 5.启动调度
        scheduler.start();
    }
}

注意:当SimpleTrigger设置的任务结束时间内,任务执行次数未全部执行时,实际任务执行次数以结束时间为准 !!!

3.CronTrigger触发器

应用场景: 特定日期(如: 2020年12月2日) / 特定周期(每个工作日的9:30 ; 每个周一,周三,周五的上午9:00至10:00之间每隔5分钟执行一次) 等。也可像SimpleTrigger一样,设置startDate和endDate。

(1)Cron Expressions——Cron 表达式

Cron表达式用来配置CronTrigger实例。Cron表达式是一个由7个子表达式组成的字符串。每个子表达式都描述了一个单独的日程细节。这些子表达式用空格分隔,分别表示:

  1. Seconds 秒
  2. Minutes 分钟
  3. Hours 小时
  4. Day-of-Month 月中的天
  5. Month 月
  6. Day-of-Week 周几
  7. Year 年(选填)
    在这里插入图片描述

1.跳转在线Cron表达式生成器点此

2.Cron表达式种特殊字符含义戳此

(2)示例代码

案例1: 实现1月22日开始执行,从0秒开始,每5秒执行一次

1.任务类

public class HelloJobCronTrigger implements Job {
    private String message;
    private Integer count;//jobDetail存储的用于计数
    
    public void setCount(Integer count) {
        this.count = count;
    }
    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // 输出当前时间
        Date date=new Date();
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //工作内容
        System.out.println("正在执行任务...时间是:"+sdf.format(date));
        
        //获取jobKey、startTime、endTime
        Trigger trigger = context.getTrigger();
        System.out.println("jobKey名称:"+trigger.getJobKey().getName()+"  ,jobGroup名称:"+trigger.getJobKey().getGroup());
        System.out.println("任务的开始时间:"+sdf.format(trigger.getStartTime())+"  ;结束时间:"+sdf.format(trigger.getEndTime()));
    }

}

2.任务调度类

public class HelloSchedulerDemo {
    public static void main(String args[]) throws SchedulerException, ParseException {
        // 1.生成调度器(Scheduler)实例,从工厂中获取调度的实例(默认:实例化new StdSchedulerFactory();)
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        //设置任务的开始/结束时间
        Date startDate = new Date();
        Date endDate = new Date();
        //设置任务的开始时间推迟3秒
        startDate.setTime(startDate.getTime()+3000);
        endDate.setTime(startDate.getTime()+10000);
        
        // 2.生成任务实例(JobDetail),加载任务Job类,与HelloJob完成绑定,要求:helloJob必须实现Job接口
        JobDetail job1 = JobBuilder.newJob(HelloJob.class)
                .withIdentity("job1", "group1")   //  参数1:任务的名称(唯一实例);参数2:任务组的名称,多个任务可以在同一个任务组中
                .build();

        
        // 3.生成触发器实例(Trigger),立即触发,每5秒钟执行一次,直到2021-1-18 14:29:00
        Trigger trigger2 = TriggerBuilder.newTrigger()
                .withIdentity("trigger2", "group1")
                .startAt(startDate)  //设置任务的开始时间
                .endAt(endDate)
                .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * 22 1 ?")) //1月22日开始执行,从0秒开始,每5秒执行一次
                .build();

        // 4.让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
        scheduler.scheduleJob(job1, trigger2);

        // 5.启动调度
        scheduler.start();
    }
}
案例2

1.任务类

public class HelloJobCronTrigger implements Job {
    private String message;
    private Integer count;//jobDetail存储的用于计数
    
    public void setCount(Integer count) {
        this.count = count;
    }
    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // 输出当前时间
        Date date=new Date();
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //工作内容
        System.out.println("正在执行任务...时间是:"+sdf.format(date));
        
        //获取jobKey、startTime、endTime
        Trigger trigger = context.getTrigger();
        System.out.println("jobKey名称:"+trigger.getJobKey().getName()+"  ,jobGroup名称:"+trigger.getJobKey().getGroup());
        System.out.println("任务的开始时间:"+sdf.format(trigger.getStartTime())+"  ;结束时间:"+sdf.format(trigger.getEndTime()));
    }

}

2.任务调度类

public class HelloSchedulerCronTriggerDemo {
    public static void main(String args[]) throws SchedulerException, ParseException, InterruptedException {
        /*1.生成调度器(Scheduler)实例,从工厂中获取调度的实例(默认:实例化new StdSchedulerFactory() )
         * 
         *法一: Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();*/
       StdSchedulerFactory schedulerFactory=new StdSchedulerFactory();
       Scheduler scheduler = schedulerFactory.getScheduler();
        
        //设置任务的开始/结束时间
        Date startDate = new Date();
        Date endDate = new Date();
        //设置任务的开始时间推迟3秒
        startDate.setTime(startDate.getTime()+3000);
        endDate.setTime(startDate.getTime()+10000);
        
        // 2.生成任务实例(JobDetail),加载任务Job类,与HelloJob完成绑定,要求:helloJob必须实现Job接口
        JobDetail job1 = JobBuilder.newJob(HelloJob.class)
                .withIdentity("job1", "group1")   //  参数1:任务的名称(唯一实例);参数2:任务组的名称,多个任务可以在同一个任务组中
                .build();

        
        // 3.生成触发器实例(Trigger),立即触发,每5秒钟执行一次,直到2021-1-18 14:29:00
        Trigger trigger2 = TriggerBuilder.newTrigger()
                .withIdentity("trigger2", "group1")
                .startAt(startDate)  //设置任务的开始时间
                .endAt(endDate)
                .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * 22 1 ?")) //1月22日开始执行,从0秒开始,每5秒执行一次
                .build();

        // 4.让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
        Date jobDate = scheduler.scheduleJob(job1, trigger2);
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("调度器的开始时间是:"+sdf.format(jobDate));
        // 5.启动调度
        scheduler.start();
        //挂起调度
        scheduler.standby();
        
        /*关闭调度,关闭后不能再开启;  shutdown(true):表示等待所有正在执行的job执行完毕后,再关闭Scheduler;  
         shutdown(false):表示直接关闭Scheduler
         scheduler.shutdown();*/
        
        //scheduler执行5秒后自动开启
        Thread.sleep(5000L);
        //重新启动任务
        scheduler.start();
        
    }
}

(3)DirectSchedulerFactory(了解)

DirectSchedulerFactory是对SchedulerFactory的直接实现,通过它可以直接构建Scheduler、threadpool等。

  DirectSchedulerFactory instance = DirectSchedulerFactory.getInstance();
  Scheduler scheduler = instance.getScheduler();

4. Quartz.properties文件

(1)参考代码


#设置默认调度程序的名称,如正在使用群集功能,则必须对群集中“逻辑上”相同的调度程序的每个实例使用相同的名称,重新赋值该值。
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
#如果您希望Quartz Scheduler通过RMI作为服务器导出本身,则为true。
org.quartz.scheduler.rmi.export = false
#如果要连接(使用)远程服务的调度程序,则为true。还必须指定RMI注册表进程的主机和端口 - 通常是“localhost”端口1099
org.quartz.scheduler.rmi.proxy = false
#org.quartz.scheduler.rmi.registryHost
#org.quartz.scheduler.rmi.registryPort
#设置这项为true使我们在调用job的execute()之前能够开始一个UserTransaction。
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false

#指定的线程池
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
#线程数量
org.quartz.threadPool.threadCount = 10
#优先级
org.quartz.threadPool.threadPriority = 5
#自创建父线程
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

#作业最大延迟时间毫秒
org.quartz.jobStore.misfireThreshold = 60000
#数据保存方式为持久化
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

(2)组成部分

  • 调度器属性

    org.quartz.scheduler.instanceName属性用来区分特定的调度器实例,可以按照功能用途来给调度器取名。
    org.quartz.scheduler.instanceId属性和前者一样,也允许任何字符串,但这个值必须在所有调度器实例中是唯一的,尤其是在一个集群环境中,作为集群的唯一key。如果你想让Quartz帮你生成这个值,可以设置为Auto。

  • 线程池属性

    threadCount:处理job的线程个数,至少是1,但最多不要超过100为宜,在多数机器上设置该值超过100的话会显得相当不实用,特别是在job执行时间较长的情况下

    threadPriority:线程的优先级,最高为10,最低为1,默认为5,优先级越高越先执行。

    org.quartz.threadPool.class:实现了org.quartz.spi.ThreadPool接口,Quartz自带的线程池实现类是org.quartz.smpl.SimpleThreadPool。

  • 作业存储设置

    描述了调度器的生命周期中,job和trigger信息时如何被存储的。

  • 插件配置

    满足特定需求用到的Quartz插件的配置。

quartz.properties配置参考一

(3)代码配置Quartz属性(了解)

除了使用quartz.properties文件对定时器进行配置之外,还通过代码配置quartz的属性。不过由于修改远不如配置文件方便,故了解即可。示例代码如下:

public class QuartzProperties {
   public static void main(String[] args) {
       //创建工厂实例
       StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();
       //创建配置工厂的属性的对象
       Properties prop=new Properties();
       //定义线程池
       //  prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
       prop.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, "org.quartz.simpl.SimpleThreadPool");
       prop.put("org.quartz.threadPool.threadCount", "5"); //必须是string型数据
    try {
        //加载配置工厂定义的属性
        stdSchedulerFactory.initialize(prop);
        
        Scheduler scheduler = stdSchedulerFactory.getScheduler();
        //开启实例
        scheduler.start();
    } catch (SchedulerException e) {
        e.printStackTrace();
    } 
   }
}

5. Quartz监听器

(1)概念

Quartz的监听器用于当任务调度中你所关注事件发生时,能够及时获取这一事件的通知。类似于任务执行过程中的邮件、短信类提醒。Quartz监听器主要有JobListener(任务监听器)TriggerListener(触发器监听器)SchedulerListener(调度器监听器) 三种。

核心思想:要创建一个listener,只需创建一个实现org.quartz.TriggerListener和/或org.quartz.JobListener接口的对象。然后,listener在运行时会向调度程序注册,并且必须给出一个名称(或者,他们必须通过他们的getName()方法来宣传自己的名字)。

listener与调度程序的ListenerManager一起注册,并配有描述listener希望接收事件的job/触发器的Matcher。

监听器的两个类型全局监听器非全局监听器,二者的区别:

  1. 全局监听器能够接收到所有的job/Trigger的事件通知
  2. 非全局监听器只能接受到在其注册的Job或Trigger的时间,没有注册的Job和Trigger则不会进行监听。

(2)JobListener

任务调度过程中,与任务Job相关的事件包括:job即将执行的通知,以及job完成执行时的通知。

1)getName() : 获取该JobListener的名称
2)jobToBeExecuted() : Scheduler在JobDetail将要被执行时调用这个方法
3)jobExecutionVetoed() : Scheduler在JobDetail即将被执行,但又被TriggerListener否决时会调用该方法
4)jobWasExecuted() : Scheduler在JobDetail被执行之后调用这个方法

1.全局JobListener

任务调度类:

 //创建并注册一个全局的job Listener
     JobDetail job1 = JobBuilder.newJob(HelloJobListener.class)
                .withIdentity("job1", "group1")   //  参数1:任务的名称(唯一实例);参数2:任务组的名称,多个任务可以在同一个任务组中
                .build();
 .........逻辑代码
 
     scheduler.getListenerManager().addJobListener(new MyJobListener(),EverythingMatcher.allJobs());

任务监听器类JobListener:

 public class HelloJobListener implements JobListener {
    private String message;
    private Integer count;// jobDetail存储的用于计数

    public void setCount(Integer count) {
        this.count = count;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // 输出当前时间
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // 工作内容
        System.out.println("正在执行任务...时间是:" + sdf.format(date));

    }

    /*
     * 获取该JobListener的名称
     */
    @Override
    public String getName() {
        String simpleName = this.getClass().getSimpleName();
        System.out.println("监听器的名称:" + simpleName);
        return simpleName;
    }

    /*
     * Scheduler在JobDetail将要被执行时调用这个方法
     */
    @Override
    public void jobToBeExecuted(JobExecutionContext context) {
        String jname = context.getJobDetail().getKey().getName();
        System.out.println("job的名称是:" + jname + "     Scheduler在JobDetail将要被执行时调用的方法");
    }

    /*
     * Scheduler在JobDetail即将被执行,但又被TriggerListener否决时会调用该方法
     */
    @Override
    public void jobExecutionVetoed(JobExecutionContext context) {
        String jname = context.getJobDetail().getKey().getName();
        System.out.println("job的名称是:" + jname + "     Scheduler在JobDetail即将被执行,但又被TriggerListener否决时会调用该方法");
    }

    /*
     * Scheduler在JobDetail被执行之后调用这个方法
     */
    @Override
    public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
        String jname = context.getJobDetail().getKey().getName();
        System.out.println("job的名称是:" + jname + "    Scheduler在JobDetail被执行之后调用这个方法");
    }

}

执行结果:
在这里插入图片描述

2.局部JobListener

任务调度类:

 //添加对特定job感兴趣的JobListener
   scheduler.getListenerManager().addJobListener(myJobListener,KeyMatcher.jobKeyEquals(new JobKey("myJobName""myJobGroup")));
   //或者
   scheduler.getListenerManager().addJobListener(myJobListener, jobKeyEquals(jobKey("myJobName", "myJobGroup")));
 // 添加对特定组的所有job感兴趣的JobListener
 scheduler.getListenerManager().addJobListener(myJobListener, jobGroupEquals("myJobGroup"));
 //添加对两个特定组的所有job感兴趣的JobListener
 scheduler.getListenerManager().addJobListener(myJobListener, or(jobGroupEquals("myJobGroup"), jobGroupEquals("yourGroup")));

任务监听器类JobListener同上一个例子

注册TriggerListeners的工作原理相同。

Quartz的大多数用户并不使用Listeners,但是当应用程序需求创建需要事件通知时不需要Job本身就必须明确地通知应用程序,这些用户就很方便。

(3)SchedulerListeners

SchedulerListeners非常类似于TriggerListeners和JobListeners,除了它们在Scheduler本身中接收到事件的通知 - 不一定与特定触发器(trigger)或job相关的事件。

与计划程序相关的事件包括:添加job/触发器,删除job/触发器,调度程序中的严重错误,关闭调度程序的通知等。
1)** jobScheduled(Trigger trigger)** : 用于部署jobDetail时调用
2)jobUnscheduled(String triggerName, String triggerGroup) : 用于卸载jobDetail时调用
3)triggerFinalized(Trigger trigger) : 当一个Trigger来到了再也不会触发的状态时调用这个方法。除非这个job已设置成了持久性,否则它就会从Scheduler中移除。
4)triggersPaused(String triggerName, String triggerGroup) : Scheduler调用这个方法是发生在一个Trigger或Trigger组被暂停时。假如是Trigger组的话,triggerName参数将为null。
5)triggersResumed(String triggerName, String triggerGroup) : Scheduler调用这个方法是发生在一个Trigger或Trigger组从暂停中恢复时。如果是Trigger组的话,triggerName参数将为null。
6)jobsPaused(String jobName, String jobGroup) : 当一个或一组jobDetail暂停时调用这个方法。
7)jobsResumed(String jobName, String jobGroup) : 当一个或一组job从暂停上恢复时调用这个方法。如果是Job组的话,jobName参数将为null。
8)schedulerError(String msg, SchedulerException cause) : 在Scheduler的正常运行期间发生严重错误时调用这个方法。
9)schedulerStarted() : 当Scheduler开启时,调用该方法。
10)schedulerInStandbyMode() : 当Scheduler处于StandBy模式时,调用该方法。
11)schedulerShutdown() : 当Scheduler停止时,调用该方法。
12)schedulingDataCleared() : 当Scheduler中的数据被清除时,调用该方法。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值