zoukankan      html  css  js  c++  java
  • 定时任务总结

      工作中总是要用到定时任务,先总结如下:

    一、java jdk自带的定时任务

      他主要由两个要素构成,是一个job和一个timer,job是执行业务逻辑的,timer是负责调度定时设定的

    job

     1 /**
     2  * 
     3  */
     4 package timer;
     5 
     6 import java.text.SimpleDateFormat;
     7 import java.util.TimerTask;
     8 
     9 /**
    10  * @author 醉逍遥
    11  *
    12  */
    13 public class MyTimerTask extends TimerTask{
    14 
    15     private String name;
    16     
    17     public MyTimerTask(String name) {
    18         this.name = name;
    19     }
    20 
    21     @Override
    22     public void run() {
    23         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    24         System.out.println("current time is: "+sdf.format(System.currentTimeMillis()));
    25         
    26     }
    27 
    28     public String getName() {
    29         return name;
    30     }
    31 
    32     public void setName(String name) {
    33         this.name = name;
    34     }
    35 }

    timer

     1 /**
     2  * 
     3  */
     4 package timer;
     5 
     6 import java.text.SimpleDateFormat;
     7 import java.util.Calendar;
     8 import java.util.Date;
     9 import java.util.Timer;
    10 
    11 /**
    12  * @author 醉逍遥
    13  *
    14  */
    15 public class MyTimer {
    16 
    17     public static void main(String[] args) throws InterruptedException {
    18         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    19         Timer timer = new Timer();
    20         MyTimerTask task = new MyTimerTask("test");
    21 //        timer.schedule(task, 3000);
    22         Calendar canlendar = Calendar.getInstance();
    23     /*    Date time = canlendar.getTime();
    24         System.out.println("first time: "+sdf.format(time));
    25         canlendar.add(Calendar.SECOND,3);
    26         System.out.println("second time: "+sdf.format(canlendar.getTime()));
    27 //        timer.schedule(task, 3000, 10000);
    28 //        timer.scheduleAtFixedRate(task, 2000, 2000);
    29         timer.scheduleAtFixedRate(task, canlendar.getTime(), 2000);*/
    30         
    31 //        注意:在格里高利历和罗马儒略历中一年中的第一个月是 JANUARY,它为 0;最后一个月取决于一年中的月份数。
    32 //        所以这个值的初始值为0,所以我们用它来表示日历月份时需要加1
    33         canlendar.set(2019, 1, 27, 15, 01, 40);
    34         System.out.println(sdf.format(canlendar.getTime()));
    35         timer.schedule(task, canlendar.getTime(), 3000);
    36     }
    37 
    38 }

     二、quartz

      quartz的三要素主要有:

    • Scheduler:调度器。所有的调度都是由它控制。
    • Trigger: 定义触发的条件。它的类型是SimpleTrigger和cronTrigger。
    • JobDetail & Job: JobDetail 定义的是任务数据,而真正的执行逻辑是在Job中, 为什么设计成JobDetail + Job,不直接使用Job?这是因为任务是有可能并发执行,如果Scheduler直接使用Job,就会存在对同一个Job实例并发访问的问题。而JobDetail & Job 方式,sheduler每次执行,都会根据JobDetail创建一个新的Job实例,这样就可以规避并发访问的问题。

      代码,首先创建maven工程导入jar包

            <!-- 定时任务框架 -->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.3.0</version>
        </dependency>

    job

     1 /**
     2  * 
     3  */
     4 package com.sharp.forward;
     5 
     6 import java.text.SimpleDateFormat;
     7 
     8 import org.quartz.Job;
     9 import org.quartz.JobDataMap;
    10 import org.quartz.JobDetail;
    11 import org.quartz.JobExecutionContext;
    12 import org.quartz.JobExecutionException;
    13 import org.quartz.JobKey;
    14 import org.quartz.Trigger;
    15 import org.quartz.TriggerKey;
    16 
    17 /**
    18  * @author 醉逍遥
    19  *
    20  */
    21 public class TestJob implements Job {
    22 
    23     @Override
    24     public void execute(JobExecutionContext context) throws JobExecutionException {
    25 //        JobExecutionContext 可以获取各种传递过来的参数进行业务逻辑的处理
    26         JobDetail jobDetail = context.getJobDetail();
    27         JobDataMap map = jobDetail.getJobDataMap();
    28         JobDataMap map1 = context.getMergedJobDataMap();
    29         System.out.println(map1.get("key1"));//合并的JobDataMap相同key第二个会覆盖第一个
    30         System.out.println(map.getBoolean("isTrue"));
    31         System.out.println(""+jobDetail.getJobDataMap().getString("key1"));//未合并的JobDataMap相同key显示的是第一个
    32         String currentTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(System.currentTimeMillis());
    33         System.out.println("currentTime: "+currentTime);
    34         System.out.println("******************************");
    35 //        通过jobDetail获取的key和通过trigger获取的jobkey是一样的
    36         Trigger trigger = context.getTrigger();
    37         JobKey jobKey = trigger.getJobKey();
    38         JobKey jobKey1 = jobDetail.getKey();
    39         System.out.println("jobDetail name: "+jobKey.getName()+"	trigger name: "+jobKey.getName());
    40         System.out.println("jobDetail class: "+jobKey.getClass()+"	trigger class: "+jobKey.getClass());
    41         System.out.println("jobDetail group: "+jobKey.getGroup()+"	trigger group: "+jobKey.getGroup());
    42 //        获取trigger的相关key
    43         TriggerKey triggerKey = trigger.getKey();
    44         System.out.println("trigger name: "+triggerKey.getName());
    45         System.out.println("trigger class: "+triggerKey.getClass());
    46         System.out.println("trigger group: "+triggerKey.getGroup());
    47     }
    48 
    49 }

    主代码

     1 /**
     2  * 
     3  */
     4 package com.sharp.forward;
     5 
     6 import java.util.Calendar;
     7 import java.util.concurrent.TimeUnit;
     8 
     9 import org.quartz.JobBuilder;
    10 import org.quartz.JobDetail;
    11 import org.quartz.Scheduler;
    12 import org.quartz.SchedulerException;
    13 import org.quartz.SchedulerFactory;
    14 import org.quartz.SimpleScheduleBuilder;
    15 import org.quartz.Trigger;
    16 import org.quartz.TriggerBuilder;
    17 import org.quartz.impl.StdSchedulerFactory;
    18 
    19 /**
    20  * @author 醉逍遥
    21  *
    22  */
    23 public class QuartzTest {
    24 
    25     /**
    26      * @param args
    27      * @throws InterruptedException 
    28      */
    29     public static void main(String[] args) throws InterruptedException {
    30         try {
    31 //            **************要素一 scheduler***********************
    32 //            创建scheduler方法一
    33             Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
    34 //            创建scheduler方法二    
    35 //            SchedulerFactory factory = new StdSchedulerFactory();
    36 //            Scheduler scheduler = factory.getScheduler();
    37 //            **************要素二 JobDetail & Job***********************
    38             JobDetail jobDetail = JobBuilder.newJob(TestJob.class).withIdentity("job1", "group1")
    39                     .usingJobData("key1", "my first jobDetial").usingJobData("double value", 9.99).build();
    40 //            **************要素三 trigger***********************
    41             Calendar calendar = null;
    42             Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group0").usingJobData("isTrue", false)
    43                     .usingJobData("key1", "my fist trigger")
    44             .startNow()
    45             .withSchedule(//设定规则
    46                     SimpleScheduleBuilder.simpleSchedule()
    47                     .withIntervalInSeconds(3)
    48                     .repeatForever()
    49                     )
    50             .build();
    51 //            执行
    52             scheduler.scheduleJob(jobDetail,trigger);
    53             System.out.println("task start-----");
    54             scheduler.start();
    55             System.out.println("task end ======");
    56             TimeUnit.MILLISECONDS.sleep(20000);
    57             scheduler.shutdown();
    58             System.out.println("scheduler shutdown");
    59          }catch (SchedulerException e) {
    60             // TODO Auto-generated catch block
    61             e.printStackTrace();
    62         }
    63     }
    64 
    65 }

    运行结果

    10:07:52.466 [main] INFO org.quartz.impl.StdSchedulerFactory - Using default implementation for ThreadExecutor
    10:07:52.471 [main] INFO org.quartz.simpl.SimpleThreadPool - Job execution threads will use class loader of thread: main
    10:07:52.483 [main] INFO org.quartz.core.SchedulerSignalerImpl - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
    10:07:52.483 [main] INFO org.quartz.core.QuartzScheduler - Quartz Scheduler v.2.3.0 created.
    10:07:52.484 [main] INFO org.quartz.simpl.RAMJobStore - RAMJobStore initialized.
    10:07:52.485 [main] INFO org.quartz.core.QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v2.3.0) 'DefaultQuartzScheduler' with instanceId 'NON_CLUSTERED'
      Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
      NOT STARTED.
      Currently in standby mode.
      Number of jobs executed: 0
      Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
      Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.
    
    10:07:52.485 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler 'DefaultQuartzScheduler' initialized from default resource file in Quartz package: 'quartz.properties'
    10:07:52.485 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler version: 2.3.0
    task start-----
    10:07:52.491 [main] INFO org.quartz.core.QuartzScheduler - Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.
    task end ======
    10:07:52.491 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
    10:07:52.493 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'group1.job1', class=com.sharp.forward.TestJob
    10:07:52.497 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
    10:07:52.497 [DefaultQuartzScheduler_Worker-1] DEBUG org.quartz.core.JobRunShell - Calling execute on job group1.job1
    my fist trigger
    false
    my first jobDetial
    currentTime: 2019-02-28 10:07:52
    ******************************
    jobDetail name: job1    trigger name: job1
    jobDetail classclass org.quartz.JobKey    trigger classclass org.quartz.JobKey
    jobDetail group: group1    trigger group: group1
    trigger name: trigger1
    trigger classclass org.quartz.TriggerKey
    trigger group: group0
    10:07:55.488 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'group1.job1', class=com.sharp.forward.TestJob
    10:07:55.489 [DefaultQuartzScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
    10:07:55.489 [DefaultQuartzScheduler_Worker-2] DEBUG org.quartz.core.JobRunShell - Calling execute on job group1.job1
    my fist trigger
    false
    my first jobDetial
    currentTime: 2019-02-28 10:07:55
    ******************************
    jobDetail name: job1    trigger name: job1
    jobDetail classclass org.quartz.JobKey    trigger classclass org.quartz.JobKey
    jobDetail group: group1    trigger group: group1
    trigger name: trigger1
    trigger classclass org.quartz.TriggerKey
    trigger group: group0
    View Code

     一些详细的概念性介绍,可以去https://www.cnblogs.com/zhanghaoliang/p/7886110.html里面看,写的比较详细,在这里只贴上一些主要的概念讲解有利于理解,其他不再累述。

    Quartz的三个基本要素

      Quartz对任务调度的领域问题进行了高度的抽象,提出了调度器、任务和触发器这3个核心的概念,并在org.quartz通过接口和类对重要的这些核心概念进行描述:

      ●Job:是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中;

      ●JobDetail:Quartz在每次执行Job时,都重新创建一个Job实例,所以它不直接接受一个Job的实例,相反它接收一个Job实现类,以便运行时通过newInstance()的反射机制实例化Job。因此需要通过一个类来描述Job的实现类及其它相关的静态信息,如Job名字、描述、关联监听器等信息,JobDetail承担了这一角色。

      通过该类的构造函数可以更具体地了解它的功用:JobDetail(java.lang.String name, java.lang.String group, java.lang.Class jobClass),该构造函数要求指定Job的实现类,以及任务在Scheduler中的组名和Job名称;

      ●Trigger:是一个类,描述触发Job执行的时间触发规则。主要有SimpleTrigger和CronTrigger这两个子类。当仅需触发一次或者以固定时间间隔周期执行,SimpleTrigger是最适合的选择;而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案:如每早晨9:00执行,周一、周三、周五下午5:00执行等;

      ●Calendar:org.quartz.Calendar和java.util.Calendar不同,它是一些日历特定时间点的集合(可以简单地将org.quartz.Calendar看作java.util.Calendar的集合——java.util.Calendar代表一个日历时间点,无特殊说明后面的Calendar即指org.quartz.Calendar)。一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点。

      假设,我们安排每周星期一早上10:00执行任务,但是如果碰到法定的节日,任务则不执行,这时就需要在Trigger触发机制的基础上使用Calendar进行定点排除。针对不同时间段类型,Quartz在org.quartz.impl.calendar包下提供了若干个Calendar的实现类,如AnnualCalendar、MonthlyCalendar、WeeklyCalendar分别针对每年、每月和每周进行定义;

      ●Scheduler:代表一个Quartz的独立运行容器,Trigger和JobDetail可以注册到Scheduler中,两者在Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据,Trigger的组及名称必须唯一,JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler定义了多个接口方法,允许外部通过组及名称访问和控制容器中Trigger和JobDetail。

      Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job。可以通过SchedulerFactory创建一个Scheduler实例。Scheduler拥有一个SchedulerContext,它类似于ServletContext,保存着Scheduler上下文信息,Job和Trigger都可以访问SchedulerContext内的信息。SchedulerContext内部通过一个Map,以键值对的方式维护这些上下文数据,SchedulerContext为保存和获取数据提供了多个put()和getXxx()的方法。可以通过Scheduler# getContext()获取对应的SchedulerContext实例;

      ●ThreadPool:Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率。

      Job有一个StatefulJob子接口,代表有状态的任务,该接口是一个没有方法的标签接口,其目的是让Quartz知道任务的类型,以便采用不同的执行方案。无状态任务在执行时拥有自己的JobDataMap拷贝,对JobDataMap的更改不会影响下次的执行。而有状态任务共享共享同一个JobDataMap实例,每次任务执行对JobDataMap所做的更改会保存下来,后面的执行可以看到这个更改,也即每次执行任务后都会对后面的执行发生影响。

      正因为这个原因,无状态的Job可以并发执行,而有状态的StatefulJob不能并发执行,这意味着如果前次的StatefulJob还没有执行完毕,下一次的任务将阻塞等待,直到前次任务执行完毕。有状态任务比无状态任务需要考虑更多的因素,程序往往拥有更高的复杂度,因此除非必要,应该尽量使用无状态的Job。

      如果Quartz使用了数据库持久化任务调度信息,无状态的JobDataMap仅会在Scheduler注册任务时保持一次,而有状态任务对应的JobDataMap在每次执行任务后都会进行保存。

      Trigger自身也可以拥有一个JobDataMap,其关联的Job可以通过JobExecutionContext#getTrigger().getJobDataMap()获取Trigger中的JobDataMap。不管是有状态还是无状态的任务,在任务执行期间对Trigger的JobDataMap所做的更改都不会进行持久,也即不会对下次的执行产生影响。

      Quartz拥有完善的事件和监听体系,大部分组件都拥有事件,如任务执行前事件、任务执行后事件、触发器触发前事件、触发后事件、调度器开始事件、关闭事件等等,可以注册相应的监听器处理感兴趣的事件。

    quartz在spring中的配置及解释:

    xml文档:

     <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">       
     <beans>       
         <bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">       
             <property name="dataSource">       
                 <ref bean="datasource"/>  <!--数据源引用指向-->  
             </property>       
             <property name="applicationContextSchedulerContextKey" value="applicationContextKey"/>     
    <!--applicationContextSchedulerContextKey: 是org.springframework.scheduling.quartz.SchedulerFactoryBean这个类中
    把spring上下 文以key/value的方式存放在了quartz的上下文中了,
    可以用applicationContextSchedulerContextKey所定义的key得到对应的spring上下文
    --> <property name="configLocation" value="classpath:quartz.properties"/> <!--quartz的配置文件的位置 --> <property name="triggers"> <list> <ref bean="trigger1"/> </list> </property> </bean> <bean id="jobDetail1" class="org.springframework.scheduling.quartz.JobDetailBean"> <property name="jobClass"> <value>继承QuartzJobBean的类的引用,如果不继承QuartzJobBean可以参考 http://www.javaeye.com/topic/486055</value> </property> </bean> <bean id="trigger1" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail" ref="jobDetail1"/> <property name="cronExpression" value="0 0/5 * ? * * *"/> </bean> </beans>

    在job中可以通过scheduler去得spring上下文信息:

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
    
        ApplicationContext applicationContext = (ApplicationContext) context.getScheduler().getContext().get("applicationContext");
    
    }

    https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/quartz/SchedulerFactoryBean.html

  • 相关阅读:
    【机器学习】关于判别模型和生成模型
    Delphi新手跟我学写CALL,附完整原程序
    QT事件研究的文章
    杂烩:QWidget、QGraphics、QtQuick
    Golang全接触
    学会使用git
    代码创建 WPF 旋转动画
    值得推荐的C/C++框架和库 very good
    可恶的QT隐式共享
    Notes on OpenSSL and Qt(ssl.pri,qsslocket_openssl_symbols.cpp)
  • 原文地址:https://www.cnblogs.com/xiaoyao-001/p/10443273.html
Copyright © 2011-2022 走看看