定時任務框架Quartz-(一)Quartz入門與Demo搭建

轉自:https://blog.csdn.net/noaman_wgs/article/details/80984873

具體代碼相結合https://www.cnblogs.com/drift-ice/p/3817269.html  裏面的代碼可以作相應的更改

什麼是Quartz?

Quartz是OpenSymphony開源組織在Job scheduling領域又一個開源項目,完全由Java開發,可以用來執行定時任務,類似於java.util.Timer。但是相較於Timer, Quartz增加了很多功能:

  • 持久性作業 - 就是保持調度定時的狀態;
  • 作業管理 - 對調度作業進行有效的管理;

大部分公司都會用到定時任務這個功能。 
拿火車票購票來說,當你下單後,後臺就會插入一條待支付的task(job),一般是30分鐘,超過30min後就會執行這個job,去判斷你是否支付,未支付就會取消此次訂單;當你支付完成之後,後臺拿到支付回調後就會再插入一條待消費的task(job),Job觸發日期爲火車票上的出發日期,超過這個時間就會執行這個job,判斷是否使用等。

在我們實際的項目中,當Job過多的時候,肯定不能人工去操作,這時候就需要一個任務調度框架,幫我們自動去執行這些程序。那麼該如何實現這個功能呢?

(1)首先我們需要定義實現一個定時功能的接口,我們可以稱之爲Task(或Job),如定時發送郵件的task(Job),重啓機器的task(Job),優惠券到期發送短信提醒的task(Job),實現接口如下: 
這裏寫圖片描述

(2)有了任務之後,還需要一個能夠實現觸發任務去執行的觸發器,觸發器Trigger最基本的功能是指定Job的執行時間,執行間隔,運行次數等。 
這裏寫圖片描述

(3)有了Job和Trigger後,怎麼樣將兩者結合起來呢?即怎樣指定Trigger去執行指定的Job呢?這時需要一個Schedule,來負責這個功能的實現。 
這裏寫圖片描述

上面三個部分就是Quartz的基本組成部分:

  • 調度器:Scheduler
  • 任務:JobDetail
  • 觸發器:Trigger,包括SimpleTrigger和CronTrigger

二、Quartz Demo搭建

下面來利用Quartz搭建一個最基本的Demo。 
1、導入依賴的jar包:

<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.0</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2、新建一個能夠打印任意內容的Job:

/**
 * Created by wanggenshen
 * Date: on 2018/7/7 16:28.
 * Description: 打印任意內容
 */
public class PrintWordsJob implements Job{

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        String printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date());
        System.out.println("PrintWordsJob start at:" + printTime + ", prints: Hello Job-" + new Random().nextInt(100));

    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

3、創建Schedule,執行任務:

/**
 * Created by wanggenshen
 * Date: on 2018/7/7 16:31.
 * Description: XXX
 */
public class MyScheduler {

    public static void main(String[] args) throws SchedulerException, InterruptedException {
        // 1、創建調度器Scheduler
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        // 2、創建JobDetail實例,並與PrintWordsJob類綁定(Job執行內容)
        JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class)
                                        .withIdentity("job1", "group1").build();
        // 3、構建Trigger實例,每隔1s執行一次
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
                .startNow()//立即生效
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(1)//每隔1s執行一次
                .repeatForever()).build();//一直執行

        //4、執行
        scheduler.scheduleJob(jobDetail, trigger);
        System.out.println("--------scheduler start ! ------------");
        scheduler.start();

        //睡眠
        TimeUnit.MINUTES.sleep(1);
        scheduler.shutdown();
        System.out.println("--------scheduler shutdown ! ------------");


    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

運行程序,可以看到程序每隔1s會打印出內容,且在一分鐘後結束: 
這裏寫圖片描述

三、Quartz核心詳解

下面就程序中出現的幾個參數,看一下Quartz框架中的幾個重要參數:

  • Job和JobDetail
  • JobExecutionContext
  • JobDataMap
  • Trigger、SimpleTrigger、CronTrigger

(1)Job和JobDetail 
Job是Quartz中的一個接口,接口下只有execute方法,在這個方法中編寫業務邏輯。 
接口中的源碼: 
這裏寫圖片描述

JobDetail用來綁定Job,爲Job實例提供許多屬性:

  • name
  • group
  • jobClass
  • jobDataMap

JobDetail綁定指定的Job,每次Scheduler調度執行一個Job的時候,首先會拿到對應的Job,然後創建該Job實例,再去執行Job中的execute()的內容,任務執行結束後,關聯的Job對象實例會被釋放,且會被JVM GC清除。

爲什麼設計成JobDetail + Job,不直接使用Job

JobDetail定義的是任務數據,而真正的執行邏輯是在Job中。 
這是因爲任務是有可能併發執行,如果Scheduler直接使用Job,就會存在對同一個Job實例併發訪問的問題。而JobDetail & Job 方式,Sheduler每次執行,都會根據JobDetail創建一個新的Job實例,這樣就可以規避併發訪問的問題。

(2)JobExecutionContext 
JobExecutionContext中包含了Quartz運行時的環境以及Job本身的詳細數據信息。 
當Schedule調度執行一個Job的時候,就會將JobExecutionContext傳遞給該Job的execute()中,Job就可以通過JobExecutionContext對象獲取信息。 
主要信息有: 
這裏寫圖片描述

(3)JobExecutionContext 
JobDataMap實現了JDK的Map接口,可以以Key-Value的形式存儲數據。 
JobDetail、Trigger都可以使用JobDataMap來設置一些參數或信息, 
Job執行execute()方法的時候,JobExecutionContext可以獲取到JobExecutionContext中的信息: 
如:

JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class)                        .usingJobData("jobDetail1", "這個Job用來測試的")
                  .withIdentity("job1", "group1").build();

 Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
      .usingJobData("trigger1", "這是jobDetail1的trigger")
      .startNow()//立即生效
      .withSchedule(SimpleScheduleBuilder.simpleSchedule()
      .withIntervalInSeconds(1)//每隔1s執行一次
      .repeatForever()).build();//一直執行
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Job執行的時候,可以獲取到這些參數信息:

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

        System.out.println(jobExecutionContext.getJobDetail().getJobDataMap().get("jobDetail1"));
        System.out.println(jobExecutionContext.getTrigger().getJobDataMap().get("trigger1"));
        String printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date());
        System.out.println("PrintWordsJob start at:" + printTime + ", prints: Hello Job-" + new Random().nextInt(100));


    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

(4)Trigger、SimpleTrigger、CronTrigger

  • Trigger

Trigger是Quartz的觸發器,會去通知Scheduler何時去執行對應Job。

new Trigger().startAt():表示觸發器首次被觸發的時間;
new Trigger().endAt():表示觸發器結束觸發的時間;
  • 1
  • 2
  • SimpleTrigger 
    SimpleTrigger可以實現在一個指定時間段內執行一次作業任務或一個時間段內多次執行作業任務。 
    下面的程序就實現了程序運行5s後開始執行Job,執行Job 5s後結束執行:
Date startDate = new Date();
startDate.setTime(startDate.getTime() + 5000);

 Date endDate = new Date();
 endDate.setTime(startDate.getTime() + 5000);

        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
                .usingJobData("trigger1", "這是jobDetail1的trigger")
                .startNow()//立即生效
                .startAt(startDate)
                .endAt(endDate)
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(1)//每隔1s執行一次
                .repeatForever()).build();//一直執行
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • CronTrigger

CronTrigger功能非常強大,是基於日曆的作業調度,而SimpleTrigger是精準指定間隔,所以相比SimpleTrigger,CroTrigger更加常用。CroTrigger是基於Cron表達式的,先了解下Cron表達式: 
由7個子表達式組成字符串的,格式如下:

[秒] [分] [小時] [日] [月] [周] [年]

Cron表達式的語法比較複雜, 
如:* 30 10 ? * 1/5 * 
表示(從後往前看) 
[指定年份] 的[ 週一到週五][指定月][不指定日][上午10時][30分][指定秒]

又如:00 00 00 ? * 10,11,12 1#5 2018 
表示2018年10、11、12月的第一週的星期五這一天的0時0分0秒去執行任務。

下面是給的一個例子: 
這裏寫圖片描述

可通過在線生成Cron表達式的工具:http://cron.qqe2.com/ 來生成自己想要的表達式。 
這裏寫圖片描述

下面的代碼就實現了每週一到週五上午10:30執行定時任務

/**
 * Created by wanggenshen
 * Date: on 2018/7/7 20:06.
 * Description: XXX
 */
public class MyScheduler2 {
    public static void main(String[] args) throws SchedulerException, InterruptedException {
        // 1、創建調度器Scheduler
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        // 2、創建JobDetail實例,並與PrintWordsJob類綁定(Job執行內容)
        JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class)
                .usingJobData("jobDetail1", "這個Job用來測試的")
                .withIdentity("job1", "group1").build();
        // 3、構建Trigger實例,每隔1s執行一次
        Date startDate = new Date();
        startDate.setTime(startDate.getTime() + 5000);

        Date endDate = new Date();
        endDate.setTime(startDate.getTime() + 5000);

        CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
                .usingJobData("trigger1", "這是jobDetail1的trigger")
                .startNow()//立即生效
                .startAt(startDate)
                .endAt(endDate)
                .withSchedule(CronScheduleBuilder.cronSchedule("* 30 10 ? * 1/5 2018"))
                .build();

        //4、執行
        scheduler.scheduleJob(jobDetail, cronTrigger);
        System.out.println("--------scheduler start ! ------------");
        scheduler.start();
        System.out.println("--------scheduler shutdown ! ------------");

    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章