本文不是用来讲授入门手把手ABC小例子的,算是自己这段时间对Timer和Quartz使用心得的总结吧,后续如果有更深的认识会不断更新的。
言归正传,想实现定时调度,最简单的方法是使用Timer 还是先给个使用范例:Java代码
long PERIOD = 60*1000;//一分钟
Timer timer = new Timer("sure's timer");
timer.schedule(new TimerTask() {
@Override
public void run() {
if(!doSomething()){
timer.cancel();
}
}
}, 0, PERIOD);
Java代码
public class Timer {
private TaskQueue queue = new TaskQueue();
private TimerThread thread = new TimerThread(queue);
}
Java代码
class TimerThread extends Thread {
private TaskQueue queue;//任务队列
public void run() {
try {
mainLoop();
} finally {
// Someone killed this Thread, behave as if Timer cancelled
synchronized(queue) {
newTasksMayBeScheduled = false;
queue.clear();
}
}
}
private void mainLoop() {
while (true) {
try {
TimerTask task;
boolean taskFired;
synchronized(queue) {
// 如果队列为空则等待
while (queue.isEmpty() && newTasksMayBeScheduled)
queue.wait();
if (queue.isEmpty())
break; // 如果不再会非空,则跳出
// 队列非空,则取第一个任务执行
long currentTime, executionTime;
task = queue.getMin();//取第一个任务
synchronized(task.lock) {
if (task.state == TimerTask.CANCELLED) {
queue.removeMin();
continue; // 任务已取消则继续取任务
}
currentTime = System.currentTimeMillis();//当前时间
executionTime = task.nextExecutionTime;//任务将开始执行的时间
if (taskFired = (executionTime<=currentTime)) {
if (task.period == 0) {
queue.removeMin();
task.state = TimerTask.EXECUTED;//已执行完成
} else { //重复执行(***)
queue.rescheduleMin(
task.period<0 ? currentTime - task.period
: executionTime + task.period);
}
}
}
if (!taskFired) // 还没到时间,则等到执行开始时间
queue.wait(executionTime - currentTime);
}
if (taskFired) // 执行
task.run();
} catch(InterruptedException e) {
}
}
}
}
Java代码
class TaskQueue {
private TimerTask[] queue = new TimerTask[128];
...
}
Java代码
public abstract class TimerTask implements Runnable {
final Object lock = new Object();//锁
public abstract void run();
public boolean cancel() {
synchronized(lock) {
boolean result = (state == SCHEDULED);
state = CANCELLED;
return result;
}
}
}
Java代码
public void schedule(TimerTask task, long delay, long period) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, System.currentTimeMillis()+delay, -period);
}
private void sched(TimerTask task, long time, long period) {
if (time < 0)
throw new IllegalArgumentException("Illegal execution time.");
synchronized(queue) {
if (!thread.newTasksMayBeScheduled)
throw new IllegalStateException("Timer already cancelled.");
synchronized(task.lock) {
if (task.state != TimerTask.VIRGIN)
throw new IllegalStateException(
"Task already scheduled or cancelled");
task.nextExecutionTime = time;//设置该任务的开始时间
task.period = period;//设置该任务的周期
task.state = TimerTask.SCHEDULED;//将该任务的状态设为SCHEDULED
}
queue.add(task);//将该任务加入到任务队列中
if (queue.getMin() == task)
queue.notify();//当列队的第一个任务是该任务时,唤醒
}
}
Job:接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义Job所运行的任务,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不同,它是一些日历特定时间点的集合。一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点
Scheduler:Trigger和JobDetail可以注册到Scheduler中,两者在Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据。Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job。可以通过SchedulerFactory创建一个Scheduler实例。Scheduler拥有一个SchedulerContext,它类似于ServletContext,保存着Scheduler上下文信息,Job和Trigger都可以访问SchedulerContext内的信息。
ThreadPool:Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率。