定时任务总结

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

一、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()+"\ttrigger name: "+jobKey.getName());
40         System.out.println("jobDetail class: "+jobKey.getClass()+"\ttrigger class: "+jobKey.getClass());
41         System.out.println("jobDetail group: "+jobKey.getGroup()+"\ttrigger 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

posted @ 2019-02-28 10:53  醉逍遥_001  阅读(615)  评论(0编辑  收藏  举报