zoukankan      html  css  js  c++  java
  • 【quartz】quartz的基本使用

    一、快速开始:

    Springboot方式:
    1. pom引入quartz包

              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-quartz</artifactId>
              </dependency>
      
    2. 写一个类,继承QuartzJobBean,并重写executeInternal方法,在这个方法中,写你想要定时任务执行的业务逻辑。

      import org.quartz.JobExecutionContext;
      import org.quartz.JobExecutionException;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.scheduling.quartz.QuartzJobBean;
      
      public class DemoJob extends QuartzJobBean {
          private static final Logger logger = LoggerFactory.getLogger(DemoJob.class);
          @Override
          protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
              logger.info("开始执行业务逻辑");
          }
      }
      
    3. 写一个类,这个类的作用是,设定定时任务执行的频率

      @Configuration
      public class GlobalSheduler {
          @Bean
          public JobDetail demoJobDetail(){
              return JobBuilder.newJob(DemoJob.class)
                      .storeDurably()
                      .build();
          }
          @Bean
          public Trigger demoJobTrigger(){
              SimpleScheduleBuilder simpleScheduleBuilder
                      = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever();
              return TriggerBuilder.newTrigger().forJob(demoJobDetail())
                      .withSchedule(simpleScheduleBuilder)
                      .build();
          }
       }
      
    4. 启动application,可以看出,定时任务成功每2秒执行一次了。

      2021-12-11 07:50:38.890 [quartzScheduler_Worker-9] INFO  com.example.jobtest.job.DemoJob - 开始执行业务逻辑
      
      2021-12-11 07:50:40.894 [quartzScheduler_Worker-10] INFO com.example.jobtest.job.DemoJob - 开始执行业务逻辑
      
      2021-12-11 07:50:42.883 [quartzScheduler_Worker-1] INFO  com.example.jobtest.job.DemoJob - 开始执行业务逻辑
      
    普通方式:
    1. 写一个实现Job接口的类,放业务逻辑。

      import org.quartz.Job;
      import org.quartz.JobExecutionContext;
      import org.quartz.JobExecutionException;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      
      public class HelloJob implements Job {
          private static final Logger logger = LoggerFactory.getLogger(HelloJob.class);
          @Override
          public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
              logger.info("开始执行业务逻辑");
          }
      }
      
    2. 写main方法,执行实现逻辑。

      import org.quartz.*;
      import org.quartz.impl.StdSchedulerFactory;
      
      public class TestHelloJob {
          public static void main(String[] args) throws SchedulerException {
              //创建一个scheduler
              Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
              scheduler.getContext().put("skey", "svalue");
      
              //创建一个Trigger
              Trigger trigger = TriggerBuilder.newTrigger()
                      .withIdentity("trigger1", "group1")
                      .usingJobData("t1", "tv1")
                      .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3)
                              .repeatForever()).build();
              trigger.getJobDataMap().put("t2", "tv2");
      
              //创建一个job
              JobDetail job = JobBuilder.newJob(HelloJob.class)
                      .usingJobData("j1", "jv1")
                      .withIdentity("myjob", "mygroup").build();
              job.getJobDataMap().put("j2", "jv2");
      
              //注册trigger并启动scheduler
              scheduler.scheduleJob(job,trigger);
              scheduler.start();
          }
      }
      
      
    3. 执行main方法,可以看到跟上次一样。

      2021-12-11 08:43:29.097 [DefaultQuartzScheduler_Worker-4] INFO  com.example.jobtest.job.HelloJob - 开始执行业务逻辑
      2021-12-11 08:43:32.093 [DefaultQuartzScheduler_Worker-5] INFO  com.example.jobtest.job.HelloJob - 开始执行业务逻辑
      2021-12-11 08:43:35.091 [DefaultQuartzScheduler_Worker-6] INFO  com.example.jobtest.job.HelloJob - 开始执行业务逻辑
      

    二、Scheduler、Job、Trigger、JobDetail都是干啥的

    1. Scheduler

      可以把这个类理解成一个执行器,它是整个定时任务的总调度,可以看见方法二中job和trigger都加入到它中,并且调用了它的start方法,定时器才能执行,这个可以类比多线程的启动方式,两种方法也可以类比多线程的两种方法。

    2. Job

      这里job的概念很简单,就是你希望执行的业务放的类,其中executeInternal()方法可以类比于Thread的run()方法,这样就好理解了。

      这里重点关注针对job有两个注解:

      关于job的状态数据(即JobDataMap)和并发性,还有一些地方需要注意。在job类上可以加入一些注解,这些注解会影响job的状态和并发性。

      @DisallowConcurrentExecution:将该注解加到job类上,告诉Quartz不要并发地执行同一个job定义(这里指特定的job类)的多个实例。请注意这里的用词。所以该限制是针对JobDetail的,而不是job类的。但是我们认为(在设计Quartz的时候)应该将该注解放在job类上,因为job类的改变经常会导致其行为发生变化。

      @PersistJobDataAfterExecution:将该注解加在job类上,告诉Quartz在成功执行了job类的execute方法后(没有发生任何异常),更新JobDetail中JobDataMap的数据,使得该job(即JobDetail)在下一次执行的时候,JobDataMap中是更新后的数据,而不是更新前的旧数据。和 @DisallowConcurrentExecution注解一样,尽管注解是加在job类上的,但其限制作用是针对job实例的,而不是job类的。由job类来承载注解,是因为job类的内容经常会影响其行为状态(比如,job类的execute方法需要显式地“理解”其”状态“)。

      如果你使用了@PersistJobDataAfterExecution注解,我们强烈建议你同时使用@DisallowConcurrentExecution注解,因为当同一个job(JobDetail)的两个实例被并发执行时,由于竞争,JobDataMap中存储的数据很可能是不确定的。

    3. JobDetail

      这个是要重点说的,先说为什么有job的情况下,还要个detail,它存在的意义是什么:

      因为scheduler在执行job的时候,实际上是每次执行都新new了一个job对象,每个job都是无状态的,执行完每个job的exec方法后,就把这个对象给丢弃了。

      验证每次执行job都是新的对象:

      2021-12-11 09:01:05.673 [DefaultQuartzScheduler_Worker-2] INFO  com.example.jobtest.job.HelloJob - 开始执行业务逻辑com.example.jobtest.job.HelloJob@213e0fad
      
      2021-12-11 09:01:08.666 [DefaultQuartzScheduler_Worker-3] INFO  com.example.jobtest.job.HelloJob - 开始执行业务逻辑com.example.jobtest.job.HelloJob@43b21dc7
      
      2021-12-11 09:01:11.667 [DefaultQuartzScheduler_Worker-4] INFO  com.example.jobtest.job.HelloJob - 开始执行业务逻辑com.example.jobtest.job.HelloJob@7f580c2e
      
      2021-12-11 09:01:14.671 [DefaultQuartzScheduler_Worker-5] INFO  com.example.jobtest.job.HelloJob - 开始执行业务逻辑com.example.jobtest.job.HelloJob@24772307
      
      

      而我们日常使用的时候,同一段逻辑,可能要接受不同参数,并根据请求参数不同执行逻辑不同。

      比如现在我们有个场景,需要根据传入参数不同打印不同日志。

      public class HelloJob implements Job {
          private static final Logger logger = LoggerFactory.getLogger(HelloJob.class);
          private String name;
          private int age;
          @Override
          public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
              System.out.println("我叫"+getName()+",我今年"+getAge()+"岁了");
              logger.info("开始执行业务逻辑"+this.toString());
          }
      
          public String getName() {
              return name;
          }
      
          public int getAge() {
              return age;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public void setAge(int age) {
              this.age = age;
          }
      }
      
              //创建一个Trigger
              Trigger trigger = TriggerBuilder.newTrigger()
                      .withIdentity("trigger1", "group1")
                      .usingJobData("t1", "tv1")
                      .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3)
                              .repeatForever()).build();
              trigger.getJobDataMap().put("t2", "tv2");
      
              //创建一个jobDetail
              JobDetail job = JobBuilder.newJob(HelloJob.class)
                      .withIdentity("myjob", "mygroup").build();
              job.getJobDataMap().put("name","王小明");
              job.getJobDataMap().put("age", 12);
      
              //创建一个Trigger
              Trigger trigger2 = TriggerBuilder.newTrigger()
                      .withIdentity("trigger2", "group1")
                      .usingJobData("t1", "tv1")
                      .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3)
                              .repeatForever()).build();
              trigger2.getJobDataMap().put("t2", "tv2");
      
              //创建一个job
              JobDetail job2 = JobBuilder.newJob(HelloJob.class)
                      .withIdentity("myjob2", "mygroup").build();
              job2.getJobDataMap().put("name","李晓红");
              job2.getJobDataMap().put("age", 15);
              scheduler.scheduleJob(job,trigger);
              scheduler.scheduleJob(job2,trigger2);
              scheduler.start();
      
      我叫王小明,我今年12岁了
      
      我叫李晓红,我今年15岁了
      

      可以看出,只多写jobDetail和trigger即可,不用再写一遍job中的内容了。而这里面主要实现的手段,就是通过jobDetail里的一个map,不管新建和销毁了多少个job,他们都可以共用jobdetail中的map里的值。

    4. Trigger

      这个可以理解成触发器,就是你定义你这个job啥时候执行,执行频率是什么样的,比如说每个月1号执行,每天凌晨执行等等。

      trigger的使用:

      Trigger trigger = newTrigger()
          .withIdentity("myTrigger", "group1")
          .startNow()
          .withSchedule(simpleSchedule()
              .withIntervalInSeconds(40)
              .repeatForever())            
          .build();
      

      withIdentity():指定自己的key和所在组,也可以不指定组,那就默认default组。

      startNow():表示立即执行,跟这个方法相对的,还有startAt(futureDate(5, IntervalUnit.MINUTE)) 或者endAt(dateOf(22, 0, 0)),表示在未来某个时间执行。

      withSchedule():这个就是调度的核心方法了,指定了你任务执行的时间和重复的策略。这个方法可以选择两种不同的参数,这两个的使用场景不同,一种是SimpleTrigger,一种是CronTrigger。

      SimpleTrigger适用于一些简单的重复场景,比如说我每隔一段固定的时间执行任务,或者在一个固定的时间执行一次任务,像我们日常用到的定时同步,或者隔一段时间发送一个消息之类的。

      CronTrigger相较于SimpleTrigger功能更加强大,它基于日历的概念而不是按照SimpleTrigger的精确指定间隔进行重新启动的作业启动计划。使用CronTrigger,你可以指定号时间表,例如“每周五中午”或“每个工作日和上午9:30”,甚至“每周一至周五上午9:00至10点之间每5分钟”和1月份的星期五“。

      这两种Trigger的具体使用,这里不做赘述,看官方文档。

      官方文档:https://www.w3cschool.cn/quartz_doc/

  • 相关阅读:
    Java并发编程(1)-Java内存模型
    Java源码解读(一) 8种基本类型对应的封装类型
    怎样修改织梦网站的favicon图标
    友情链接:图片链接或文字链接
    FCKeditor 添加行距、字体功能 (转载)
    如何解决织梦DedeCMS后台模块管理列表不显示
    制作兼容各种版本浏览器的新闻图片焦点切换效果
    织梦添加站内搜索
    织梦友情链接正确的调用方法和技巧
    织梦后台添加友情链接的方法(flink标签)
  • 原文地址:https://www.cnblogs.com/pandaNHF/p/15678114.html
Copyright © 2011-2022 走看看