【技术人生】教程——Quartz入门

Quartz是一个基于Java的开源的企业级任务调度框架,能够支持从数十到上万个Job的任务调度,并且Job被定义为一个个单独的组件,降低了框架的侵入性,提高了代码复用度,方便迁移。


作为一个企业级的任务调度框架,Quartz拥有一系列企业级特性:

1. 支持Job的持久化

2. 支持事务机制

3. 支持分布式集群

4. 支持各种Job/Trigger行为相关的监听器

5. 支持插件机制,可以为Quartz添加其它功能

前言

不使用框架,要实现一个定时任务,比如说每隔1s执行一次,最直接的办法是这样做:

while (true) {
  // do something
  Thread.sleep(1000);
}

也就是通过让线程睡眠的方式实现定时的功能,但是这种方法有几个问题:

1. 只能实现像每隔一段时间执行一次这样简单的定时任务,对于其它形式就无能为力了,比如在每天的某些时刻执行

2. 任务的执行和调度放在一块,耦合度太高,不方便管理和扩展


其实JDK本身就为了我们提供了工具来实现简单的定时任务调度,比如Timer。Timer提供了一个简单的任务调度框架,被调度的任务可以只执行一次,或每隔一段时间执行一次。在Timer的背后,其实是通过一个线程去执行定时任务。一个线程可以执行多个定时任务,但是每个任务的执行不能花过多的时间,不然就会妨碍其它任务的执行。


Timer的基本使用方式:

// 定义调度任务
class MyTask extends TimerTask {
        @Override
        public void run() {
            System.out.println("executing...");
// Timer对象
Timer timer = new Timer();
// 调度执行任务
timer.schedule(new MyTask(), 1000, 2000);

其中schedule方法签名为:

/**
* @param task : 定时任务
* @param delay : 延迟多久开始调度任务
* @param peroid : 每次执行任务之间的间隔时间
public void schedule(TimerTask task, long delay, long period)


Timer的问题是:

1. 同样不能灵活设置任务的执行时间

2. 一个Timer调度的任务之间会相互影响,只要其中一个任务没有捕获抛出的异常,就会导致其它的任务也会被终止


而且,自JDK5.0之后,JDK在并发包下提供了另一个任务调度框架来代替Timer——ScheduledThreadPoolExecutor,它属于线程池的一种实现,因此可以提供一个线程池来执行定时任务。基本使用方式:

// 创建一个线程池任务调度服务
ScheduledExecutorService executorService =
            Executors.newScheduledThreadPool(1);
// 定义调度任务
class MyTask implements Runnable {
        @Override
        public void run() {
            System.out.println("executing...");
// 调度任务
executorService.scheduleAtFixedRate(new MyTask(),
1000,
TimeUnit.SECONDS);

并且ScheduledThreadPoolExecutor提供几种任务的调度方式:

// 简单的延迟执行Runnable类型的任务
public ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);
// 简单的延迟执行Callable类型的任务
public <V> ScheduledFuture<V> schedule(Callable<V> callable,long delay,TimeUnit unit);
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit);


ScheduledThreadPoolExecutor已经足够满足简单定时任务的调度和执行了,不管是从性能还是扩展性上讲,只有其无法满足需求时,才需要用到像Quartz这样成熟的任务调度框架。


基本用法

调度一个任务

加入Maven依赖:

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>${quartz.version}</version>
</dependency>
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz-jobs</artifactId>
    <version>${quartz.version}</version>
</dependency>

定义调度任务类:

class HelloJob implements Job {
  @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
      System.out.printlin("hello world");

调度任务:

// 实例化一个调度器工厂,每个应用只有唯一一个工厂实例
  SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();
  // 实例化一个调度器
  Scheduler sched = schedFact.getScheduler();
  // 启动,只有启动了调度器Quartz才会去执行任务
  sched.start();
  // 实例化一个任务
  JobDetail job = newJob(HelloJob.class)
      .withIdentity("myJob", "group1")
      .build();
  // 实例化一个任务触发器,立刻触发,每40s执行一次
  Trigger trigger = newTrigger()
      .withIdentity("myTrigger", "group1")
      .startNow()
      .withSchedule(simpleSchedule()