logo是黑色 怎么调网站的色调,北海 做网站 英文,上传网站程序是什么,淘宝网站开发多少金额概述
定时器在应用广泛#xff0c;比如定时统计数据生成报表、每隔设定的时间提醒用户等。Java.util包自带的定时器Timer提供简单的定点执行功能#xff0c;而Quartz是一个第三方提供的定时器框架。
对比
Timer 优点#xff1a; java.util包自带的#xff0c;Timer的任务…概述
定时器在应用广泛比如定时统计数据生成报表、每隔设定的时间提醒用户等。Java.util包自带的定时器Timer提供简单的定点执行功能而Quartz是一个第三方提供的定时器框架。
对比
Timer 优点 java.util包自带的Timer的任务是通过创建TimerTask子类进行实现使用方便。 缺点 定时器没有持久性机制。定时器不能灵活的调度只能设置开始时间和重复间隔没有基于日期一天中的时间等定时器不使用线程池每个定时器一个线程定时器没有真正的管理方案必须编写自己的机制管理。 Quartz 优点 Quartz是一个作业调度库可以与任何其他软件系统集成也可以和其他软件系统一起使用。Quartz非常灵活可以灵活、准确的控制日期节点以及执行次数。Quartz非常轻量级只需要很少的配置即可完成需求“开箱即用”。 缺点 Quartz必须要新建一个class文件实现Job接口重写execute方法定义任务。
使用方法
Timer
Timer的任务是通过创建TimerTask子类进行实现定时器由类Timer提供常见功能如下
schedule(TimerTask task, Date time)在time时间点执行task任务一次。schedule(TimerTask task, long delay)在延迟delay毫秒后执行task任务一次。schedule(TimerTask task, Date firstTime, long period)在firsttime时间点执行task一次之后定期period毫秒时间执行task。时间如果为过去时间, 不会执行过去没有执行的任务, 但是会马上执行。schedule(TimerTask task, long delay, long period)在延迟delay后执行task一次之后定期period毫秒时间执行task。时间如果为过去时间, 不会执行过去没有执行的任务, 但是会马上执行。
所有delay和period都是long类型的延迟时间单位为毫秒。
指定开始时间
package 定时器;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;public class TimerDemo {public static void main(String[] args) {method();System.out.println(main执行完成);}public static void method() {// 1、创建Timer对象用于定义定时器的任务及开始时间、周期Timer timer new Timer();// 2、创建匿名内部类定义任务TimerTask task new TimerTask() {int count 1;Overridepublic void run() {System.out.println(Thread.currentThread().getName() 指定时间定时任务执行中count (count));}};// 3.任务调度timer.schedule(task, new Date());}
}运行结果 结果看出main执行完成后推出而定时任务另起线程执行等待。
指定开始时间及执行周期
开始时间为当前时间每一秒执行一次。
public class TimerDemo {public static void main(String[] args) {method();method2();System.out.println(main执行完成);}public static void method() {// 1、创建Timer对象用于定义定时器的任务及开始时间、周期Timer timer new Timer();// 2、创建匿名内部类定义任务TimerTask task new TimerTask() {int count 1;Overridepublic void run() {System.out.println(Thread.currentThread().getName() 指定时间定时任务执行中count (count));}};// 3.任务调度timer.schedule(task, new Date());}public static void method2() {// 1.创建Timer对象用于定义定时器的任务及开始时间、周期Timer timer new Timer();// 创建匿名内部类定义任务TimerTask task new TimerTask() {int count2 1;Overridepublic void run() {System.out.println(Thread.currentThread().getName() 指定时间循环定时任务执行中count2 (count2));}};// 2.任务调度 毫秒值timer.schedule(task, new Date(), 1000);}
}运行结果 另起线程执行每间隔一秒执行一次。
延期执行及执行周期
延迟4秒后执行每秒执行一次。
public class TimerDemo {public static void main(String[] args) {method();method2();method3();System.out.println(main执行完成);}public static void method() {...}public static void method2() {...}public static void method3() {// 1.创建Timer对象用于定义定时器的任务及开始时间、周期Timer timer new Timer();// 创建匿名内部类定义任务TimerTask task new TimerTask() {int count3 1;Overridepublic void run() {System.out.println(Thread.currentThread().getName() 延时循环定时任务执行中count3 (count3));}};// 2.任务调度timer.schedule(task, 4000, 1000);}
}运行结果 另起线程执行延迟4秒后执行每间隔一秒执行一次。
Quartz
Quartz执行需要1、创建一个SchedulerFactory对象用于生产调度器-Scheduler对象2、创建调度所需要的任务 任务-Job3、指定开始的时间和执行周期 触发器-Trigger。
需要jar包quartz-*.jar、slf4j-api-*.jar
示例
package QuartzTest;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import static org.quartz.JobBuilder.newJob;public class TestQuartz {public static void main(String[] args) throws Exception{//1、创建工厂对象用于生产调度器Scheduler对象Scheduler scheduler new StdSchedulerFactory().getScheduler();//2、创建任务(JobDetail)具体的任务需要自定义类实现Job接口JobDetail jobDetail newJob(MailJob.class) //指定干活的类MailJob.withIdentity(mailjob1, mailgroup) //定义任务名称和分组.usingJobData(email, admin10086.com) //定义属性.build();//3、定义触发器Trigger设置开始的时间及周期Trigger trigger TriggerBuilder.newTrigger().withIdentity(trigger1, group1) //定义名称和所属的租.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1) //每隔2秒执行一次.withRepeatCount(10)) //总共执行11次(第一次执行不基数).startNow().build();//4、调度器指定要执行的任务JobDetail及触发器Triggerscheduler.scheduleJob(jobDetail, trigger);//5、启动scheduler.start();//6、等待15秒让前面的任务都执行完了之后再关闭调度器Thread.sleep(15000);scheduler.shutdown(true);System.out.printf(Thread.currentThread().getName() main关闭);}
}自定义任务类MailJob实现Job接口
package 定时器;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.text.SimpleDateFormat;
import java.util.Date;public class MailJob implements Job {public void execute(JobExecutionContext context) throws JobExecutionException {JobDetail detail context.getJobDetail();String email detail.getJobDataMap().getString(email);SimpleDateFormat sdf new SimpleDateFormat(HH:mm:ss);String now sdf.format(new Date());System.out.printf([Thread.currentThread().getName()] new Date() 给邮件地址 %s 发出了一封定时邮件, 当前时间是: %s%n ,email, now);}
}运行结果
.withIdentity(“mailjob1”, “mailgroup”)用于分组。 withIdentity定义任务名称mailjob1和组名mailgroup。比如一个系统有3个job 是备份数据库的有4个job 是发邮件的那么对他们进行分组可以方便管理类似于一次性停止所有发邮件的这样的操作。
任务-JobDetail
调度所需要的任务-JobDetail。需要新建一个类实现Job接口重写execute方法定义任务。 JobDetail描述这个Job是做什么的。 JobDataMap: 给Job提供参数用的。通过JobDetail.getJobDataMap获取
getString(String key)获取参数值put(String key, String value)设置参数值
public class TestQuartz {public static void main(String[] args) throws Exception{...//2、创建任务(JobDetail)具体的任务需要自定义类实现Job接口JobDetail jobDetail newJob(MailJob.class) //指定干活的类MailJob.withIdentity(mailjob1, mailgroup) //定义任务名称和分组.usingJobData(email, admin10086.com) //定义属性.build();//用JobDataMap 修改emailjobDetail.getJobDataMap().put(email, admintaobao.com);...}
}输出
Job 并发
Quartz定时任务默认都是并发执行的无论上一次任务是否结束或者完成只要间隔时间到就会执行下一次, 因为如果定时任执行太长会长时间占用资源导致其它任务堵塞。
数据执行任务DatabaseBackupJob
package QuartzTest;
import org.quartz.*;
import java.util.Date;public class DatabaseBackupJob implements Job {public void execute(JobExecutionContext context) throws JobExecutionException {JobDetail detail context.getJobDetail();String database detail.getJobDataMap().getString(database);System.out.printf([Thread.currentThread().getName()] new Date() 给数据库 %s 备份, 耗时10秒 %n ,database);try {Thread.sleep(10000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}TestQuartz
public class TestQuartz {public static void main(String[] args) throws Exception{databaseCurrentJob();}private static void databaseCurrentJob() throws Exception {Scheduler scheduler StdSchedulerFactory.getDefaultScheduler();Trigger trigger newTrigger().withIdentity(trigger1, group1).startNow().withSchedule(simpleSchedule().withIntervalInSeconds(2).withRepeatCount(10)).build();//定义一个JobDetailJobDetail jobDetail newJob(DatabaseBackupJob.class).withIdentity(backupjob, databasegroup).usingJobData(database, how2java).build();//调度加入这个jobscheduler.scheduleJob(jobDetail, trigger);//启动scheduler.start();//等待100秒让前面的任务都执行完了之后再关闭调度器Thread.sleep(100000);scheduler.shutdown(true);}
}运行结果 由结果看出任务并没有等一个任务执行完成再执行下一个任务。而是等待2秒就执行下一个任务。 但是有时候会做长时间的任务比如上述数据库备份这个时候就希望上一次备份成功结束之后才开始下一次备份即便是规定时间到了也不能开始因为这样很有可能造成数据库被锁死 几个线程同时备份数据库引发无法预计的混乱。
那怎么实现呢给任务增加注解 DisallowConcurrentExecution 数据执行任务DatabaseBackupJob
package QuartzTest;
import org.quartz.*;
import java.util.Date;DisallowConcurrentExecution
public class DatabaseBackupJob implements Job {public void execute(JobExecutionContext context) throws JobExecutionException {...}
}执行结果 由结果看出任务会等前一个任务执行完成执行10秒才会执行。
Job 异常
任务里发生异常是很常见的。 异常处理办法通常是两种
setUnscheduleAllTriggers当异常发生那么就通知所有管理这个 Job 的调度停止运行它。setRefireImmediately当异常发生修改一下参数马上重新运行。 ExceptionJob1
public class ExceptionJob1 implements Job {public void execute(JobExecutionContext context) throws JobExecutionException {int i 0;try {//故意发生异常System.out.println(100/i);} catch (Exception e) {System.out.println([Thread.currentThread().getName()] new Date() 发生了异常取消这个Job 对应的所有调度);JobExecutionException je new JobExecutionException(e);je.setUnscheduleAllTriggers(true);throw je;}}
}ExceptionJob2
public class ExceptionJob2 implements Job {static int i 0;public void execute(JobExecutionContext context) throws JobExecutionException {try {//故意发生异常System.out.println([Thread.currentThread().getName()] new Date() 运算结果100/i);} catch (Exception e) {System.out.println([Thread.currentThread().getName()] new Date() 发生了异常修改一下参数立即重新执行);i 1;JobExecutionException je new JobExecutionException(e);je.setRefireImmediately(true);throw je;}}
}TestQuartz
public class TestQuartz {public static void main(String[] args) throws Exception{exceptionHandle1();//exceptionHandle2();}private static void exceptionHandle1() throws Exception {Scheduler scheduler StdSchedulerFactory.getDefaultScheduler();Trigger trigger newTrigger().withIdentity(trigger1, group1).startNow().withSchedule(simpleSchedule().withIntervalInSeconds(2).withRepeatCount(5)).build();//定义一个JobDetailJobDetail jobDetail newJob(ExceptionJob1.class).withIdentity(exceptionJob1, someJobGroup).build();//调度加入这个jobscheduler.scheduleJob(jobDetail, trigger);//启动scheduler.start();//等待20秒让前面的任务都执行完了之后再关闭调度器Thread.sleep(10000);scheduler.shutdown(true);}private static void exceptionHandle2() throws Exception {Scheduler scheduler StdSchedulerFactory.getDefaultScheduler();Trigger trigger newTrigger().withIdentity(trigger1, group1).startNow().withSchedule(simpleSchedule().withIntervalInSeconds(2).withRepeatCount(5)).build();//定义一个JobDetailJobDetail jobDetail newJob(ExceptionJob2.class).withIdentity(exceptionJob1, someJobGroup).build();//调度加入这个jobscheduler.scheduleJob(jobDetail, trigger);//启动scheduler.start();//等待20秒让前面的任务都执行完了之后再关闭调度器Thread.sleep(10000);scheduler.shutdown(true);}
}运行结果 执行exceptionHandle2()
public class TestQuartz {public static void main(String[] args) throws Exception{//exceptionHandle1();exceptionHandle2();}
}运行结果
中断 Job
在业务上有时候需要中断任务那么这个Job需要实现 InterruptableJob 接口才可以被中断。 StoppableJob
package QuartzTest;
import org.quartz.InterruptableJob;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.UnableToInterruptJobException;import java.util.Date;//必须实现InterruptableJob 而非 Job才能够被中断
public class StoppableJob implements InterruptableJob {private boolean stop false;public void execute(JobExecutionContext context) throws JobExecutionException {while(true){if(stop)break;try {System.out.println([Thread.currentThread().getName()] new Date() 每隔1秒进行一次检测看看是否停止);Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println([Thread.currentThread().getName()] new Date() 持续工作中。。。);}}public void interrupt() throws UnableToInterruptJobException {System.out.println([Thread.currentThread().getName()] new Date() 被调度叫停);stop true;}
}TestQuartz
public class TestQuartz {public static void main(String[] args) throws Exception{stop();}private static void stop() throws Exception {Scheduler scheduler StdSchedulerFactory.getDefaultScheduler();Trigger trigger newTrigger().withIdentity(trigger1, group1).startNow().build();//定义一个JobDetailJobDetail jobDetail newJob(StoppableJob.class).withIdentity(exceptionJob1, someJobGroup).build();//调度加入这个jobscheduler.scheduleJob(jobDetail, trigger);//启动scheduler.start();Thread.sleep(5000);System.out.println(过5秒调度停止 job);//key 就相当于这个Job的主键scheduler.interrupt(jobDetail.getKey());//等待20秒让前面的任务都执行完了之后再关闭调度器Thread.sleep(10000);scheduler.shutdown(true);}}运行结果
触发器-Trigger
指定开始的时间和执行周期。Trigger 就是触发器的意思用来指定什么时间开始触发触发多少次每隔多久触发一次。 常见触发器 SimpleTrigger、CronTrigger。
SimpleTrigger
常见方法
withIdentity(String name, String group)设置触发器名称、组名startNow()立即执行startAt(Date triggerStartTime)指定时间点执行
10秒后运行
DateBuilder.futureDate()可以方便的获取10秒后 5分钟后 3个小时候2个月后这样的时间。 QuartzDemo
public class QuartzDemo {public static void main(String[] args) throws SchedulerException {//1、创建工厂对象用于生产调度器Scheduler对象Scheduler scheduler new StdSchedulerFactory().getScheduler();Date startTime DateBuilder.futureDate(10, DateBuilder.IntervalUnit.SECOND);//2、创建任务(JobDetail)具体的任务需要自定义类实现Job接口JobDetail jobDetail JobBuilder.newJob(MailJob.class) //指定干活的类MailJob.withIdentity(mailjob1, mailgroup) //定义任务名称和分组.usingJobData(email, admin10086.com) //定义属性.build();//3、定义触发器Trigger设置开始的时间及周期SimpleTrigger trigger (SimpleTrigger) newTrigger().withIdentity(trigger1, group1).startAt(startTime).build();//4、调度器指定要执行的任务JobDetail及触发器TriggerDate ft scheduler.scheduleJob(jobDetail, trigger);System.out.println(当前时间是 new Date().toLocaleString());System.out.printf(%s 这个任务会在 %s 准时开始运行累计运行%d次间隔时间是%d毫秒%n, job.getKey(), ft.toLocaleString(), trigger.getRepeatCount()1, trigger.getRepeatInterval());//5、启动scheduler.start();}
}运行结果
累计n次间隔n秒
withSchedule withIntervalInSeconds(n) 每隔n秒执行一次withRepeatCount(n)) 总共执行n1次(第一次执行不基数)repeatForever()无限重复
QuartzDemo
...
/3、定义触发器Trigger设置开始的时间及周期SimpleTrigger trigger (SimpleTrigger) newTrigger().withIdentity(trigger1, group1).startAt(startTime).withSchedule(simpleSchedule().withRepeatCount(3).withIntervalInSeconds(1)).build();运行结果
CronTrigger
Cron 是Linux下的一个定时器功能很强大但是表达式更为复杂。CronTrigger 就是用Cron表达式来安排触发时间和次数的。
Cron表达式见《Cron表达式》
每隔2秒执行一次
...
//3、定义触发器Trigger设置开始的时间及周期CronTrigger trigger (CronTrigger) newTrigger().withIdentity(trigger1, group1).withSchedule(cronSchedule(0/2 * * * * ?)).build();运行结果
调度器-Scheduler
Date scheduleJob(JobDetail var1, Trigger var2)将任务和触发器加入调度器start()启动shutdown()关闭
监听器
Quartz的监听器有Job监听器、Trigger监听器、Scheduler监听器对不同层面进行监控。实际业务用的较多的是Job监听器用于监听器是否执行了其他的用的相对较少本知识主要讲解Job的。 监听器功能需要创建实现了 JobListener 接口的监听器类。 JobListener接口方法如下
public String getName()返回JobListener名称。对于注册为全局的监听器getName()主要用于记录日志对于由特定Job引用的 JobListener注册在 JobDetail 上的监听器名称必须匹配从监听器上getName()返回值。public void jobToBeExecuted(JobExecutionContext jobExecutionContext)Scheduler在 JobDetail 将要被执行时调用的方法。public void jobExecutionVetoed(JobExecutionContext jobExecutionContext)Scheduler在 JobDetail即将被执行但又被 Triggeristener否决了调用的方法。public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e)Scheduler在 JobDetail 被执行之后调用的方法。
邮件监听器MailJobListener
package 定时器;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;public class MailJobListener implements JobListener {Overridepublic String getName() {return listener of mail job;}Overridepublic void jobToBeExecuted(JobExecutionContext jobExecutionContext) {System.out.println(准备执行\t jobExecutionContext.getJobDetail().getKey());}Overridepublic void jobExecutionVetoed(JobExecutionContext jobExecutionContext) {System.out.println(取消执行\t jobExecutionContext.getJobDetail().getKey());}Overridepublic void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) {System.out.println(执行结束\t jobExecutionContext.getJobDetail().getKey());System.out.println();}
}运行结果
数据库存储
Quartz的触发器、调度、任务等信息都是放在内存中的。不能对执行进度进行实时查看而且一旦系统异常信息就会丢失。 所以Quartz还提供了另一个方式可以把这些信息存放在数据库中叫做 JobStoreTX。运行状态信息存放在数据库中。
建表
DROP DATABASE IF EXISTS quartz;
CREATE DATABASE quartz DEFAULT CHARACTER SET utf8;
USE quartz;DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;CREATE TABLE QRTZ_JOB_DETAILS(SCHED_NAME VARCHAR(120) NOT NULL,JOB_NAME VARCHAR(100) NOT NULL,JOB_GROUP VARCHAR(100) NOT NULL,DESCRIPTION VARCHAR(250) NULL,JOB_CLASS_NAME VARCHAR(250) NOT NULL,IS_DURABLE VARCHAR(1) NOT NULL,IS_NONCONCURRENT VARCHAR(1) NOT NULL,IS_UPDATE_DATA VARCHAR(1) NOT NULL,REQUESTS_RECOVERY VARCHAR(1) NOT NULL,JOB_DATA BLOB NULL,PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);CREATE TABLE QRTZ_TRIGGERS(SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_NAME VARCHAR(100) NOT NULL,TRIGGER_GROUP VARCHAR(100) NOT NULL,JOB_NAME VARCHAR(100) NOT NULL,JOB_GROUP VARCHAR(100) NOT NULL,DESCRIPTION VARCHAR(250) NULL,NEXT_FIRE_TIME BIGINT(13) NULL,PREV_FIRE_TIME BIGINT(13) NULL,PRIORITY INTEGER NULL,TRIGGER_STATE VARCHAR(16) NOT NULL,TRIGGER_TYPE VARCHAR(8) NOT NULL,START_TIME BIGINT(13) NOT NULL,END_TIME BIGINT(13) NULL,CALENDAR_NAME VARCHAR(100) NULL,MISFIRE_INSTR SMALLINT(2) NULL,JOB_DATA BLOB NULL,PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
);CREATE TABLE QRTZ_SIMPLE_TRIGGERS(SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_NAME VARCHAR(100) NOT NULL,TRIGGER_GROUP VARCHAR(100) NOT NULL,REPEAT_COUNT BIGINT(7) NOT NULL,REPEAT_INTERVAL BIGINT(12) NOT NULL,TIMES_TRIGGERED BIGINT(10) NOT NULL,PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);CREATE TABLE QRTZ_CRON_TRIGGERS(SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_NAME VARCHAR(100) NOT NULL,TRIGGER_GROUP VARCHAR(100) NOT NULL,CRON_EXPRESSION VARCHAR(100) NOT NULL,TIME_ZONE_ID VARCHAR(80),PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);CREATE TABLE QRTZ_SIMPROP_TRIGGERS( SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_NAME VARCHAR(100) NOT NULL,TRIGGER_GROUP VARCHAR(100) NOT NULL,STR_PROP_1 VARCHAR(512) NULL,STR_PROP_2 VARCHAR(512) NULL,STR_PROP_3 VARCHAR(512) NULL,INT_PROP_1 INT NULL,INT_PROP_2 INT NULL,LONG_PROP_1 BIGINT NULL,LONG_PROP_2 BIGINT NULL,DEC_PROP_1 NUMERIC(13,4) NULL,DEC_PROP_2 NUMERIC(13,4) NULL,BOOL_PROP_1 VARCHAR(1) NULL,BOOL_PROP_2 VARCHAR(1) NULL,PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);CREATE TABLE QRTZ_BLOB_TRIGGERS(SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_NAME VARCHAR(100) NOT NULL,TRIGGER_GROUP VARCHAR(100) NOT NULL,BLOB_DATA BLOB NULL,PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);CREATE TABLE QRTZ_CALENDARS(SCHED_NAME VARCHAR(120) NOT NULL,CALENDAR_NAME VARCHAR(100) NOT NULL,CALENDAR BLOB NOT NULL,PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS(SCHED_NAME VARCHAR(120) NOT NULL,TRIGGER_GROUP VARCHAR(100) NOT NULL,PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);CREATE TABLE QRTZ_FIRED_TRIGGERS(SCHED_NAME VARCHAR(120) NOT NULL,ENTRY_ID VARCHAR(95) NOT NULL,TRIGGER_NAME VARCHAR(100) NOT NULL,TRIGGER_GROUP VARCHAR(100) NOT NULL,INSTANCE_NAME VARCHAR(100) NOT NULL,FIRED_TIME BIGINT(13) NOT NULL,SCHED_TIME BIGINT(13) NOT NULL,PRIORITY INTEGER NOT NULL,STATE VARCHAR(16) NOT NULL,JOB_NAME VARCHAR(100) NULL,JOB_GROUP VARCHAR(100) NULL,IS_NONCONCURRENT VARCHAR(1) NULL,REQUESTS_RECOVERY VARCHAR(1) NULL,PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);CREATE TABLE QRTZ_SCHEDULER_STATE(SCHED_NAME VARCHAR(120) NOT NULL,INSTANCE_NAME VARCHAR(100) NOT NULL,LAST_CHECKIN_TIME BIGINT(13) NOT NULL,CHECKIN_INTERVAL BIGINT(13) NOT NULL,PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);CREATE TABLE QRTZ_LOCKS(SCHED_NAME VARCHAR(120) NOT NULL,LOCK_NAME VARCHAR(40) NOT NULL,PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);commit;配置文件
Quartz默认加载工程目录下的quartz.properties如果工程目录下没有就会去加载quartz.jar包下面的quartz.properties文件。 故在src下新建 quartz.properties 配置文件里面指定使用 JobStoreTX 方式管理任务。
org.quartz.jobStore.class org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.tablePrefix QRTZ_
org.quartz.scheduler.instanceName MyScheduler
org.quartz.threadPool.threadCount 3
org.quartz.jobStore.class org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix QRTZ_
org.quartz.jobStore.dataSource mysqlDatabaseorg.quartz.dataSource.mysqlDatabase.driver com.mysql.jdbc.Driver
org.quartz.dataSource.mysqlDatabase.URL jdbc:mysql://localhost:3306/quartz?characterEncodingutf-8
org.quartz.dataSource.mysqlDatabase.user root
org.quartz.dataSource.mysqlDatabase.password admin
org.quartz.dataSource.mysqlDatabase.maxConnections 5MailJob
和以前的一样没什么变化。
package 定时器;
import org.quartz.*;
import java.text.SimpleDateFormat;
import java.util.Date;DisallowConcurrentExecution
public class MailJob implements Job {public void execute(JobExecutionContext context) throws JobExecutionException {JobDetail detail context.getJobDetail();String email detail.getJobDataMap().getString(email);SimpleDateFormat sdf new SimpleDateFormat(HH:mm:ss);String now sdf.format(new Date());System.out.printf([Thread.currentThread().getName()] new Date() 给邮件地址 %s 发出了一封定时邮件, 当前时间是: %s(%s) %n ,email, now,context.isRecovering());}
}TestQuartz
新增加了一个resumeJobFromDatabase 方法当使用原来的方式增加任务报异常的时候就直接从数据库重跑任务。
package 定时器;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import static org.quartz.TriggerBuilder.newTrigger;public class QuartzDemo {public static void main(String[] args) throws Exception {try {assginNewJob();} catch (ObjectAlreadyExistsException e) {System.err.println(发现任务已经在数据库存在了直接从数据库里运行: e.getMessage());// TODO Auto-generated catch blockresumeJobFromDatabase();}}private static void resumeJobFromDatabase() throws Exception {Scheduler scheduler StdSchedulerFactory.getDefaultScheduler();scheduler.start();// 等待200秒让前面的任务都执行完了之后再关闭调度器Thread.sleep(200000);scheduler.shutdown(true);}private static void assginNewJob() throws SchedulerException, InterruptedException {// 创建调度器Scheduler scheduler StdSchedulerFactory.getDefaultScheduler();// 定义一个触发器Trigger trigger newTrigger().withIdentity(trigger1, group1) // 定义名称和所属的租.startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(15) // 每隔15秒执行一次.withRepeatCount(10)) // 总共执行11次(第一次执行不基数).build();// 定义一个JobDetailJobDetail job JobBuilder.newJob(MailJob.class) // 指定干活的类MailJob.withIdentity(mailjob1, mailgroup) // 定义任务名称和分组.usingJobData(email, admin10086.com) // 定义属性.build();// 调度加入这个jobscheduler.scheduleJob(job, trigger);// 启动scheduler.start();// 等待20秒让前面的任务都执行完了之后再关闭调度器Thread.sleep(20000);scheduler.shutdown(true);}
}第一次运行 QRTZ_SIMPLE_TRIGGERS QRTZ_TRIGGERS QRTZ_JOB_DETAILS
第二次运行 注意如果任务执行完成上述数据库表数据均被清空。如果在执行期间查看数据库表表QRTZ_TRIGGERS、QRTZ_JOB_DETAILS数据不变表QRTZ_SIMPLE_TRIGGERS会实时记录执行次数 字段REPEAT_COUNT记录还需要执行的总次数字段TIMES_TRIGGERED记录执行过的次数。
Quartz集群
所谓的Quartz集群是指在基于数据库存储Quartz调度信息的基础上有多个一模一样的 Quartz 应用在运行。 当某一个Quartz应用重启或者发生问题的时候其他的Quartz应用会借助数据库这个桥梁探知到它不行了从而接手把该进行的Job调度工作进行下去。 以这种方式保证任务调度的高可用性即在发生异常重启等情况下调度信息依然连贯性地进行下去就好像Quartz应用从来没有中断过似的。
quartz.properties
quartz.properties 在原来的基础上增加3行
org.quartz.jobStore.isClustered true
org.quartz.scheduler.instanceId AUTO
org.quartz.jobStore.clusterCheckinInterval 1000...org.quartz.jobStore.isClustered true开启集群org.quartz.scheduler.instanceId AUTO要进行集群多个应用调度id instanceId 必须不一样这里使用AUTO就会自动分配不同的ID。 目测是本机机器名称加上时间戳org.quartz.jobStore.clusterCheckinInterval 1000每个一秒钟去数据库检查一下在其他应用挂掉之后及时补上
注要进行集群多个应用调度名称 instanceName 应该是一样的。
TestQuartz
TestQuartz 不需要做改动本例增加一些输出信息。 启动步骤
启动一次 TestQuartz叫做 a 应用紧接着在几秒钟内再次启动 TestQuartz叫做 b 应用使用多控制台显示方式在两个不同的控制台观察现象
上述相当于两个应用做了集群。 运行结果 应用a 应用b 应用a先执行运行20秒就自动结束了。 应用b在应用a执行期间不会执行。 应用b在应用b执行结束之后检测到任务还未完成自动把后续任务执行完毕。