quartz源码-Schedule启动过程分析

演示demo

public class HelloJob implements Job {

    private String j1;

    public void setJ1(String j1) {
        this.j1 = j1;
    }

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("************任务开始执行***********");

        System.out.println(j1);

        System.out.println("任务执行结束!");
    }
}
public class Main {
    public static void main(String[] args) throws Exception {
        JobDetail job1 = newJob(HelloJob.class)
        // 往JobDataMap中传入数据
        .usingJobData("j1", "我是job1")
        // 给JobDetail定义一个唯一标识,group+name唯一确定一个JobDetail
        .withIdentity("job1", "group1")
        .build();

        Trigger trigger = newTrigger()
        // 给Trigger定义一个唯一标识,group+name唯一确定一个Trigger
        .withIdentity("trigger1", "group1")
        // 往JobDataMap中传入数据
        .usingJobData("t1", "v1")
        // 规定Trigger触发时间,这里是每秒触发一次
        .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever())
        .build();

        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        // 把Trigger和JobDetail绑定起来
        scheduler.scheduleJob(job1, trigger);
        scheduler.start();
    }
}

我们主要分析这三行代码

Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.scheduleJob(job1, trigger);
scheduler.start();

Schedule的获取

Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

我们跳转到StdSchedulerFactory涉及到的代码,主要操作如下:

  1. 解析quartz.properties文件,把其中的数据存储到变量PropertiesParser cfg;中
  2. **获取Scheduler。**会从SchedulerRepository获取Scheduler;获取不到就会通过instantiate()实例化一个新的Scheduler,这个新的Scheduler中的属性的值就是从前面变量PropertiesParser cfg中获取,实例化的Schedule会存储到SchedulerRepository中去。
public class StdSchedulerFactory implements SchedulerFactory {

    public static Scheduler getDefaultScheduler() throws SchedulerException {
        StdSchedulerFactory fact = new StdSchedulerFactory();
        return fact.getScheduler();
    }

    public Scheduler getScheduler() throws SchedulerException {
        if (cfg == null) {
            // 解析quartz.properties文件,存储到变量PropertiesParser cfg;中
            initialize();
        }

        SchedulerRepository schedRep = SchedulerRepository.getInstance();
        Scheduler sched = schedRep.lookup(getSchedulerName());
        if (sched != null) {
            if (sched.isShutdown()) {
                schedRep.remove(getSchedulerName());
            } else {
                return sched;
            }
        }
        
        // 实例化Scheduler
        sched = instantiate();
        return sched;
    }
}

我们看到SchedulerRepository是个单例模式,用HashMap<String, Scheduler> schedulers;变量存储所有实例化的scheduler,能够保证获取到scheduler唯一性。

public class SchedulerRepository {

    private HashMap<String, Scheduler> schedulers;

    private static SchedulerRepository inst;
    
    private SchedulerRepository() {
        schedulers = new HashMap<String, Scheduler>();
    }

    public static synchronized SchedulerRepository getInstance() {
        if (inst == null) {
            inst = new SchedulerRepository();
        }
        return inst;
    }
    
    public synchronized Scheduler lookup(String schedName) {
        return schedulers.get(schedName);
    }
}

job和trigger的绑定和存储

scheduler.scheduleJob(job1, trigger);

我们跳转到StdScheduler类中,会发现StdScheduler类什么也没做,只是把其中的方法转去调用QuartzScheduler类中对应的方法。

public class StdScheduler implements Scheduler { 
    private QuartzScheduler sched;

    public Date scheduleJob(JobDetail jobDetail, Trigger trigger) throws SchedulerException {
        return sched.scheduleJob(jobDetail, trigger);
    }
}

来到QuartzScheduler类中对应方法,主要操作如下:

  1. 把trigger和job建立绑定关系
  2. 计算trigger的第一次触发时间
  3. 存储trigger和job
public class QuartzScheduler implements RemotableQuartzScheduler {
    // 包含创建QuartzScheduler实例所需的所有资源(JobStore、ThreadPool等)。
    private QuartzSchedulerResources resources;
    
    public Date scheduleJob(JobDetail jobDetail,
                            Trigger trigger) throws SchedulerException {
        validateState();
        if (jobDetail == null) {
            throw new SchedulerException("JobDetail cannot be null");
        }
        if (trigger == null) {
            throw new SchedulerException("Trigger cannot be null");
        }
        if (jobDetail.getKey() == null) {
            throw new SchedulerException("Job's key cannot be null");
        }
        if (jobDetail.getJobClass() == null) {
            throw new SchedulerException("Job's class cannot be null");
        }
        OperableTrigger trig = (OperableTrigger)trigger;
        // 1.把trigger和job建立绑定关系
        // 判断trigger是不是已经绑定了其他job,没有绑定直接绑定给定的job
        if (trigger.getJobKey() == null) {
            trig.setJobKey(jobDetail.getKey());
        } else if (!trigger.getJobKey().equals(jobDetail.getKey())) {
            throw new SchedulerException(
                "Trigger does not reference given job!");
        }
        trig.validate();
        
        // 2.计算第一次点火时间
        Calendar cal = null;
        if (trigger.getCalendarName() != null) {
            cal = resources.getJobStore().retrieveCalendar(trigger.getCalendarName());
        }
        Date ft = trig.computeFirstFireTime(cal);
        if (ft == null) {
            throw new SchedulerException(
                "Based on configured schedule, the given trigger '" + trigger.getKey() + "' will never fire.");
        }
        
        // 3.存储JobDetail和Trigger
        resources.getJobStore().storeJobAndTrigger(jobDetail, trig);
        notifySchedulerListenersJobAdded(jobDetail);
        notifySchedulerThread(trigger.getNextFireTime().getTime());
        notifySchedulerListenersSchduled(trigger);
    
        return ft;
    }
}

启动

scheduler.start();

跳转到StdScheduler

public class StdScheduler implements Scheduler {

    private QuartzScheduler sched;
    
    public void start() throws SchedulerException {
        sched.start();
    }
}

跳转到QuartzScheduler#start()方法。这个方法主要是唤醒QuartzSchedulerThread主线程

public void start() throws SchedulerException {

    if (shuttingDown|| closed) {
        throw new SchedulerException(
                "The Scheduler cannot be restarted after shutdown() has been called.");
    }

    // QTZ-212 : calling new schedulerStarting() method on the listeners
    // right after entering start()
    notifySchedulerListenersStarting();

    if (initialStart == null) {
        initialStart = new Date();
        this.resources.getJobStore().schedulerStarted();            
        startPlugins();
    } else {
        resources.getJobStore().schedulerResumed();
    }
    // 唤醒QuartzSchedulerThread主线程
    schedThread.togglePause(false);

    getLog().info("Scheduler " + resources.getUniqueIdentifier() + " started.");
    
    notifySchedulerListenersStarted();
}

这个QuartzSchedulerThread类会在QuartzScheduler实例化的时候就会被创建并开始运行。

public class QuartzScheduler implements RemotableQuartzScheduler {
    public QuartzScheduler(QuartzSchedulerResources resources, long idleWaitTime, @Deprecated long dbRetryInterval)
        throws SchedulerException {
        // ......(省略其他代码)
        this.schedThread = new QuartzSchedulerThread(this, resources);
        ThreadExecutor schedThreadExecutor = resources.getThreadExecutor();
        schedThreadExecutor.execute(this.schedThread);
        // ......(省略其他代码)
    }	
}

跳转到QuartzSchedulerThread#run()方法,这个线程开始执行时,paused && !halted.get()这个条件一直满足,会不断执行sigLock.wait(1000L)。直到togglePause(false)调用时,就会改变paused=false,同时会唤醒sigLock.wait(1000L)这个地方,跳出while (paused && !halted.get())这个循环,去触发定时任务。

public class QuartzSchedulerThread extends Thread {
    private boolean paused;

    private final Object sigLock = new Object();

    private AtomicBoolean halted;

    void togglePause(boolean pause) {
        synchronized (sigLock) {
            paused = pause;
            if (paused) {
                signalSchedulingChange(0);
            } else {
                sigLock.notifyAll();
            }
        }
    }
    
    @Override
    public void run() {
        int acquiresFailed = 0;

        while (!halted.get()) {
            try {
                // 检查是否需要暂停,当QuartzScheduleThread实例化时,paused=true,进入等待,
                // 直到调用QuartzScheduler#start()方法时会调用sigLock.notifyAll()。唤醒该线程
                synchronized (sigLock) {
                    while (paused && !halted.get()) {
                        try {
                            // wait until togglePause(false) is called...
                            sigLock.wait(1000L);
                        } catch (InterruptedException ignore) {
                        }

                        // 暂停时重置失败计数器,这样我们就不用在取消暂停后再次等待
                        acquiresFailed = 0;
                    }

                    if (halted.get()) {
                        break;
                    }
                }
                // ......省略其他代码
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值