Quartz使用(2) - Quartz核心接口Scheduler、Job


quartz的核心接口如下:

接口 含義
Scheduler scheduler的主要API接口
Job 任務實現接口,期望調度器能夠執行
JobDetail 用於定義Job實例
Trigger 調度器基於特定時間來執行指定任務的組件
JobBuilder   用於定義、創建JobDetail實例
TriggerBuilder 用於定義、創建Trigger實例

1. Scheduler

一個調度器的生命周期為通過SchedulerFactory創建,直到執行其shutdown()方法。當Scheduler創建之后,可以進行增加、刪除及顯示任務Job與觸發器Trigger,並且執行其他的調度相關的操作,如暫停一個觸發器Trigger。需要注意的是,直到調用start()方法時,Scheduler才正式開始執行job和trigger。

StdSchedulerFactory用於創建Scheduler,其依賴於一系列的屬性來決定如何產生Scheduler。可以通過四種途徑向StdSchedulerFactory提供屬性配置信息。

1) 通過java.util.Properties實例提供

package org.ws.quartz.test2;

import java.util.Properties;

import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SchedulerExample {
    
    private static Logger logger = LoggerFactory.getLogger(SchedulerExample.class);
    
    public static void main(String[] args) {
        // 創建工廠實例
        StdSchedulerFactory factory = new StdSchedulerFactory();
        
        // 創建配置工廠的屬性對象
        Properties props = new Properties();
        props.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, "org.quartz.simpl.SimpleThreadPool"); // 線程池定義
        props.put("org.quartz.threadPool.threadCount", "10"); // 默認Scheduler的線程數
        
        try {
            // 使用定義的屬性初始化工廠
            factory.initialize(props);
            
            Scheduler scheduler = factory.getScheduler();
            
            scheduler.start();
            logger.info("scheudler started, metadata: "+scheduler.getMetaData());
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}
View Code

執行后的結果:

2017-07-09 15:15:17 [INFO]-[org.ws.quartz.test2.SchedulerExample] scheudler started, metadata: Quartz Scheduler (v2.2.1) 'QuartzScheduler' with instanceId 'NON_CLUSTERED'
  Scheduler class: 'org.quartz.impl.StdScheduler' - running locally.
  Running since: Sun Jul 09 15:15:17 CST 2017
  Not 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.

  
View Code

可以看到對應的配置屬性已經生效。

通過Properties設置工廠屬性的缺點在用硬編碼,假如需要修改例子中線程數量,將不得不修改代碼,然后重新編譯。后面幾種方法可以解決硬編碼的問題。

2) 通過外部屬性文件提供

使用方法:

public void initialize(String filename) throws SchedulerException;

3) 通過含有屬性文件內容的java.io.InputStream提供

使用方法:

public void initialize(InputStream propertiesStream) throws SchedulerException;

4) quartz.properties配置文件【推薦

如果調用無參的initialize方法,StdSchedulerFactory會試圖從quartz.properties的文件中加載。quartz.properties相關配置后續文章會介紹,注意quartz.properties的加載順序為:

a. 檢查System.getProperty("org.quartz.properties")中是否設置其他屬性文件名

b. 如果a未設置,則將會從當前工作目錄中加載quartz.properties配置文件

c. 如果b未找到,則試圖從系統的classpath中加載該配置文件。

Scheduler在生命周期中也可執行其他操作,如查詢、設置standby模式、繼續執行、停止執行。standby模式會導致Scheduler暫時停止查找Job去執行。standby模式的設置直接使用scheudler.standby()即可。

Scheduler的停止方法為shutdown()方法,也可以使用有參shutdown(false),其中參數表示是否讓當前正在進行的job正常執行完成才停止Scheduler

2. Job

Job即為為你執行一個任務的Java類。該任務可以是java編碼的任何功能,如使用JavaMail發送郵件、創建遠程接口並調用EJB上的方法等。

Java類僅需要實現org.quartz.job接口,將所需要實現的功能放在其execute方法中。execute方法的定義如下:

public void execute(JobExecutionContext context) throws JobExecutionException;

其中JobExecutionContext對象讓Job能訪問Quartz運行時環境的所有信息和Job本身的明細數據。運行時環境信息包括注冊到Scheduler上與該Job相關聯的JobDetail和Trigger。

例:Quartz使用(1) - 初識quartz示例中HelloWordJob獲取運行環境時信息如下:

package org.ws.quartz.test1;

import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloWorldJob implements Job{
    
    private static Logger logger = LoggerFactory.getLogger(HelloWorldJob.class);
    
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        
        logger.info("Hello World");
        
        // 每一個Job都有其自己所屬的JobDetail
        JobDetail jobDetail = context.getJobDetail();
        
        // JobDetail的名稱和組名
        logger.info("Name and Group: "+jobDetail.getKey());
        
        // 獲取Scheduler
        Scheduler scheduler = context.getScheduler();
        try {
            logger.info("Scheduler name: "+scheduler.getSchedulerName());
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        
        logger.info("job class: "+jobDetail.getJobClass());
        
        // 任務執行的時間
        logger.info("Job fired at "+context.getFireTime());
        
        // 任務下一次執行的時間
        logger.info("Job nexe fire time: "+context.getNextFireTime());
    }
}
View Code

運行結果如下:

2017-07-09 16:00:59 [INFO]-[org.ws.quartz.test1.SimpleQuartzExample] init scheduler componets
  2017-07-09 16:00:59 [INFO]-[org.ws.quartz.test1.SimpleQuartzExample] execute scheduler
  2017-07-09 16:00:59 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Hello World
  2017-07-09 16:00:59 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Name and Group: HelloWorld_Group.HelloWorld_Job
  2017-07-09 16:00:59 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Scheduler name: DefaultQuartzScheduler
  2017-07-09 16:00:59 [INFO]-[org.ws.quartz.test1.HelloWorldJob] job class: class org.ws.quartz.test1.HelloWorldJob
  2017-07-09 16:00:59 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Job fired at Sun Jul 09 16:00:59 CST 2017
  2017-07-09 16:00:59 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Job nexe fire time: Sun Jul 09 16:01:09 CST 2017
  2017-07-09 16:01:09 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Hello World
  2017-07-09 16:01:09 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Name and Group: HelloWorld_Group.HelloWorld_Job
  2017-07-09 16:01:09 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Scheduler name: DefaultQuartzScheduler
  2017-07-09 16:01:09 [INFO]-[org.ws.quartz.test1.HelloWorldJob] job class: class org.ws.quartz.test1.HelloWorldJob
  2017-07-09 16:01:09 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Job fired at Sun Jul 09 16:01:09 CST 2017
  2017-07-09 16:01:09 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Job nexe fire time: Sun Jul 09 16:01:19 CST 2017
  2017-07-09 16:01:19 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Hello World
  2017-07-09 16:01:19 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Name and Group: HelloWorld_Group.HelloWorld_Job
  2017-07-09 16:01:19 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Scheduler name: DefaultQuartzScheduler
  2017-07-09 16:01:19 [INFO]-[org.ws.quartz.test1.HelloWorldJob] job class: class org.ws.quartz.test1.HelloWorldJob
  2017-07-09 16:01:19 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Job fired at Sun Jul 09 16:01:19 CST 2017
  2017-07-09 16:01:19 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Job nexe fire time: Sun Jul 09 16:01:29 CST 2017
  2017-07-09 16:01:29 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Hello World
  2017-07-09 16:01:29 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Name and Group: HelloWorld_Group.HelloWorld_Job
  2017-07-09 16:01:29 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Scheduler name: DefaultQuartzScheduler
  2017-07-09 16:01:29 [INFO]-[org.ws.quartz.test1.HelloWorldJob] job class: class org.ws.quartz.test1.HelloWorldJob
  2017-07-09 16:01:29 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Job fired at Sun Jul 09 16:01:29 CST 2017
  2017-07-09 16:01:29 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Job nexe fire time: Sun Jul 09 16:01:39 CST 2017
  2017-07-09 16:01:39 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Hello World
  2017-07-09 16:01:39 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Name and Group: HelloWorld_Group.HelloWorld_Job
  2017-07-09 16:01:39 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Scheduler name: DefaultQuartzScheduler
  2017-07-09 16:01:39 [INFO]-[org.ws.quartz.test1.HelloWorldJob] job class: class org.ws.quartz.test1.HelloWorldJob
  2017-07-09 16:01:39 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Job fired at Sun Jul 09 16:01:39 CST 2017
  2017-07-09 16:01:39 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Job nexe fire time: Sun Jul 09 16:01:49 CST 2017
  2017-07-09 16:01:49 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Hello World
  2017-07-09 16:01:49 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Name and Group: HelloWorld_Group.HelloWorld_Job
  2017-07-09 16:01:49 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Scheduler name: DefaultQuartzScheduler
  2017-07-09 16:01:49 [INFO]-[org.ws.quartz.test1.HelloWorldJob] job class: class org.ws.quartz.test1.HelloWorldJob
  2017-07-09 16:01:49 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Job fired at Sun Jul 09 16:01:49 CST 2017
  2017-07-09 16:01:49 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Job nexe fire time: Sun Jul 09 16:01:59 CST 2017
  2017-07-09 16:01:59 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Hello World
  2017-07-09 16:01:59 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Name and Group: HelloWorld_Group.HelloWorld_Job
  2017-07-09 16:01:59 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Scheduler name: DefaultQuartzScheduler
  2017-07-09 16:01:59 [INFO]-[org.ws.quartz.test1.HelloWorldJob] job class: class org.ws.quartz.test1.HelloWorldJob
  2017-07-09 16:01:59 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Job fired at Sun Jul 09 16:01:59 CST 2017
  2017-07-09 16:01:59 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Job nexe fire time: Sun Jul 09 16:02:09 CST 2017
  2017-07-09 16:01:59 [INFO]-[org.ws.quartz.test1.SimpleQuartzExample] shut down scheduler
  
View Code

3. JobDetail

3.1 Job簡述

JobDetail是作為Job實例進行定義的,注意部署在Scheduler上的每一個Job只創建一個JobDetail實例。且需要注意的是注冊到Scheduler上的不是Job對象,而是JobDetail實例。

Job 的實例要到該執行它們的時候才會實例化出來。每次 Job 被執行,一個新的 Job 實例會被創建。其中暗含的意思就是你的 Job 不必擔心線程安全性,因為同一時刻僅有一個線程去執行給定 Job 類的實例,甚至是並發執行同一 Job 也是如此

可以使用JobDataMap來定義Job的狀態,JobDataMap中可以存入key-value對,這些數據可以在Job實現類中進行傳遞和訪問。這是向你的Job傳送配置信息的便捷方法。

Job 能通過 JobExecutionContext 對象訪問 JobDataMap

例:在Quartz使用(1) - 初識quartz示例中,可以在等待時間修改為20s, SimpleQuartzExample.createJobDetail方法修改為:

protected JobDetail createJobDetail(){
        return JobBuilder.newJob(HelloWorldJob.class) // 待執行的任務
                .withIdentity("HelloWorld_Job", "HelloWorld_Group") // 名稱與組名組成Scheduler中任務的唯一標識
                .usingJobData("message", "welcom to study quartz") // 存儲Job的狀態信息
                .build(); // 構建
    }
View Code

同時HelloWorldJob中的execute方法修改如下:

public void execute(JobExecutionContext context) throws JobExecutionException {
        logger.info("Hello World, "+context.getJobDetail().getJobDataMap().get("message"));
    }
View Code

執行結果:

2017-07-09 16:17:51 [INFO]-[org.ws.quartz.test1.SimpleQuartzExample] init scheduler componets
  2017-07-09 16:17:51 [INFO]-[org.ws.quartz.test1.SimpleQuartzExample] execute scheduler
  2017-07-09 16:17:51 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Hello World, welcom to study quartz
  2017-07-09 16:18:01 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Hello World, welcom to study quartz
  2017-07-09 16:18:11 [INFO]-[org.ws.quartz.test1.HelloWorldJob] Hello World, welcom to study quartz
  2017-07-09 16:18:11 [INFO]-[org.ws.quartz.test1.SimpleQuartzExample] shut down scheduler
View Code

 3.2 有狀態Job和無狀態Job

有狀態的Job可以理解為多次Job調用期間可以持有一些狀態信息,這些狀態信息存儲在JobDataMap中,而默認的無狀態job每次調用時都會創建一個新的JobDataMap

有狀態的Job示例:

調度主方法:

package org.ws.quartz.test1;

import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SimpleQuartzExample {
    
    private static Logger logger = LoggerFactory.getLogger(SimpleQuartzExample.class);
    
    public static void main(String[] args) throws SchedulerException, InterruptedException {
        
        SimpleQuartzExample exam = new SimpleQuartzExample();
        
        logger.info("init scheduler componets");
        
        // 創建任務
        JobDetail jobDetail = exam.createJobDetail();
        
        // 創建觸發器
        Trigger trigger = exam.createTrigger();
        
        // 創建調度器
        Scheduler scheduler = exam.createScheduler();

        // 構建調度任務
        scheduler.scheduleJob(jobDetail, trigger);
        
        logger.info("execute scheduler");
        // 開啟調度器
        scheduler.start();
        
        // 一分鍾后關閉調度器
        Thread.sleep(20000);
        scheduler.shutdown();
        
        logger.info("shut down scheduler");
    }

    protected Scheduler createScheduler() throws SchedulerException{
        return StdSchedulerFactory.getDefaultScheduler(); 
    }
    
    protected JobDetail createJobDetail(){
        return JobBuilder.newJob(HelloWorldJob.class) // 待執行的任務
                .withIdentity("HelloWorld_Job", "HelloWorld_Group") // 名稱與組名組成Scheduler中任務的唯一標識
                .usingJobData("count", 0) // 將count初始化為0
                .build(); // 構建
    }
    
    protected Trigger createTrigger(){
        return  TriggerBuilder.newTrigger()
                .withIdentity("HelloWorld_Trigger", "HelloWorld_Group") // 名稱與組名組成Scheduler中觸發器的唯一標識
                .withSchedule(
                        SimpleScheduleBuilder.simpleSchedule() // 創建SimpleTrigger
                        .withIntervalInSeconds(10) // 10秒間隔
                        .repeatForever() // 重復循環
                        ).build(); // 構建
    }
}
View Code

HelloWorldJob方法:

package org.ws.quartz.test1;

import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@PersistJobDataAfterExecution
public class HelloWorldJob implements Job{
    
    private static Logger logger = LoggerFactory.getLogger(HelloWorldJob.class);
    
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        
        int count = jobDataMap.getInt("count");
        logger.info("count: "+count);
        
        ++count;
        jobDataMap.put("count", count);
    }
}
View Code

執行結果:

2017-07-09 16:38:55 [INFO]-[org.ws.quartz.test1.SimpleQuartzExample] init scheduler componets
  2017-07-09 16:38:55 [INFO]-[org.ws.quartz.test1.SimpleQuartzExample] execute scheduler
  2017-07-09 16:38:55 [INFO]-[org.ws.quartz.test1.HelloWorldJob] count: 0
  2017-07-09 16:39:05 [INFO]-[org.ws.quartz.test1.HelloWorldJob] count: 1
  2017-07-09 16:39:15 [INFO]-[org.ws.quartz.test1.HelloWorldJob] count: 2
  2017-07-09 16:39:15 [INFO]-[org.ws.quartz.test1.SimpleQuartzExample] shut down scheduler
  
View Code

如果不增加@PersistJobDataAfterExecution注解,運行結果為:

2017-07-09 16:40:58 [INFO]-[org.ws.quartz.test1.SimpleQuartzExample] init scheduler componets
  2017-07-09 16:40:59 [INFO]-[org.ws.quartz.test1.SimpleQuartzExample] execute scheduler
  2017-07-09 16:40:59 [INFO]-[org.ws.quartz.test1.HelloWorldJob] count: 0
  2017-07-09 16:41:08 [INFO]-[org.ws.quartz.test1.HelloWorldJob] count: 0
  2017-07-09 16:41:18 [INFO]-[org.ws.quartz.test1.HelloWorldJob] count: 0
  2017-07-09 16:41:19 [INFO]-[org.ws.quartz.test1.SimpleQuartzExample] shut down scheduler
View Code

可見 @PersistJobDataAfterExecution的作用在於持久化保存在JobDataMap中的傳遞參數,使得多次執行Job,可以獲取傳遞參數的狀態信息。

3.3 @DisallowConcurrentExecution  

quartz中另一個常用的注解為@DisallowConcurrentExecution,該注解可以同一個時刻,同一個任務只能執行一次,不能並行執行兩個或多個同一任務。但需要注意的是,多個不同的任務是可以同時執行的

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM