最近在进行数据迁移的任务(就是迁移历史数据),数据量很大,需要运行几个月才能运行完。要求是每天的非工作时间定时进行迁移,正常上班的时间自动停止。自然就想到了使用Quartz来执行定时任务。每天定时的启动任务是没有问题的,Quartz本来就是干这个事情的。但是如何让正在执行的任务定时关闭呢?
通过在网上搜查资料发现了停止执行任务的代码如下:
public static void removeJob(String jobName, String jobGroupName,
String triggerName, String triggerGroupName) {
try {
SchedulerFactory gSchedulerFactory = new StdSchedulerFactory();
Scheduler sched = gSchedulerFactory.getScheduler();
sched.pauseTrigger(triggerName, triggerGroupName);// 停止触发器
sched.unscheduleJob(triggerName, triggerGroupName);// 移除触发器
sched.interrupt(jobName, jobGroupName);
sched.deleteJob(jobName, jobGroupName);// 删除任务
} catch (Exception e) {
throw new RuntimeException(e);
}
}
但是这个代码是有问题的,因为这样只是把定时任务从调度器中删除了,到下次触发的时刻不会再触发而已,因为调度器中已经没有这个定时任务了嘛。但是并不会把当前正在执行的任务给停止。就拿我的数据迁移来说吧,我每天需要在早上八点把迁移任务停止,每天6点下班之后启动。既然每天还需要触发定时任务,肯定不能把定时任务按照上边的方式删除了。
那现在的问题就是如何才能把迁移任务定时关闭了呢,这就需要我们从我们的迁移程序中去操作了。当当前的时间是8点的时候,我进行一个状态值得修改,根据这个状态值来进行迁移任务的结束。
由于保密原因,下面我只能列出样例代码:
1.操作时间的工具类:
package com.ykp.quartz.test;
import java.util.Calendar;
import java.util.Date;
/** * @function:操作时间的工具类 * @author yankunpeng * @date 2015-3-6上午11:45:44 * @mailto yan095650@163.com */
public class DateUtils {
/** * @function:判断当前时间是否在两个时间之间 * @param currDate * @param startDate * @param endDate * @return boolean * @author yankunpeng * @date 2015-3-5下午03:56:28 * @mailto yan095650@163.com */
public static boolean betweenTime(Date currDate, Date startDate,
Date endDate) {
boolean b = false;
try {
if (currDate.getTime() >= startDate.getTime()
&& currDate.getTime() <= endDate.getTime()) {
b = true;
}
} catch (Exception e) {
e.printStackTrace();
}
return b;
}
/** * @function:获取指定hour小时的前后minute分钟的时间 * @param hour * @param minute * @return Date * @author yankunpeng * @date 2015-3-6上午11:44:49 * @mailto yan095650@163.com */
public static Date getAfterMinutesDate(int hour, int minute) {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, hour); // 控制时
calendar.set(Calendar.MINUTE, minute); // 控制分
calendar.set(Calendar.SECOND, 0); // 控制秒
Date time = calendar.getTime();
return time;
}
}
2.调度器工具类:
package com.ykp.quartz.test;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;
/** * @function:调度器工具类 * @author yankunpeng * @date 2015-3-6下午02:11:50 * @mailto yan095650@163.com */
public class QuartzUtils {
private static SchedulerFactory gSchedulerFactory = new StdSchedulerFactory();
/** * @function:添加一个定时任务 * @param jobName * @param jobGroupName * @param triggerName * @param triggerGroupName * @param jobClass * @param time * void * @author yankunpeng * @date 2015-3-6下午02:19:34 * @mailto yan095650@163.com */
@SuppressWarnings("unchecked")
public static void addJob(String jobName, String jobGroupName,
String triggerName, String triggerGroupName, Class jobClass,
String time) {
try {
Scheduler sched = gSchedulerFactory.getScheduler();
JobDetail jobDetail = new JobDetail(jobName, jobGroupName, jobClass);// 任务名,任务组,任务执行类
// 触发器
CronTrigger trigger = new CronTrigger(triggerName, triggerGroupName);// 触发器名,触发器组
trigger.setCronExpression(time);// 触发器时间设定
sched.scheduleJob(jobDetail, trigger);
// 启动
if (!sched.isShutdown()) {
sched.start();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
3.调度任务类:
package com.ykp.quartz.test;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/** * @function:调度任务类 * @author yankunpeng * @date 2015-3-6上午11:06:07 * @mailto yan095650@163.com */
public class QuartzJob implements Job {
public static boolean stop = false;// 停止任务的标志,默认是打开
private static int stoptime = 14;// 停止的起始时间
private static int before = 8;// 提前分钟数,可以是所有整数
private static int delay = 9;// 延迟分钟数,可以是所有整数
private static Date currDate;// 当前时间
private static Date startDate;// 开始时间,通过stoptime和before计算出来的
private static Date endDate;// 结束时间,通过stoptime和delay计算出来的
static {
currDate = new Date();
startDate = DateUtils.getAfterMinutesDate(stoptime, before);
endDate = DateUtils.getAfterMinutesDate(stoptime, delay);
}
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
try {
final String jobName = arg0.getJobDetail().getName();
int i = 0;
int count = 1000000;// 这个是满足迁移的数据条数,实际应该从数据库中查询的
while ((!stop) && i < count) {
int num = 1000;// 每次从数据库查询最前边的1000条,相当于分页查询,但又不是分页查询,因为每次查询时我们的数据是在减少
for (int j = 0; j < num; j++) {
// 实际上这里应该使用while,但是为了演示,所有使用for
try {
System.out.println("任务:"
+ jobName
+ "-->"
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(new Date()) + ".........");
Thread.sleep(1 * 100);
i++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 判断当前的时间是都在startDate和endDate之间,如果在,则把停止任务的标志位修改成true,这样迁移任务就结束了
currDate = new Date();
stop = DateUtils.betweenTime(currDate, startDate, endDate);
if (stop) {
System.out.println("停止迁移任务:" + jobName);
}
}
} catch (Exception e) {
System.out.println(e);
} finally {
stop = false;// 最后记得把标记恢复初始值,等待迁移任务的下次执行
}
}
}
4.测试类:
package com.ykp.quartz.test;
/** * @function:测试调度器 * @author yankunpeng * @date 2015-3-6下午02:20:18 * @mailto yan095650@163.com */
public class QuartzTest {
public static void main(String[] args) {
try {
String jobName = "动态任务调度";
String jobGroupName = "任务组名";
String triggerName = "触发器名";
String triggerGroupName = "触发器组名";
String time = "0 07,10 14 * * ?";
QuartzUtils.addJob(jobName, jobGroupName, triggerName,
triggerGroupName, QuartzJob.class, time);
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行测试类,运行之前可以设置触发规则:0 07,10 14 * * ?,意思是在14:07和14:10时刻分别触发
private static int stoptime = 14;// 停止的起始时间
private static int before = 8;// 提前分钟数,可以是所有整数
private static int delay = 9;// 延迟分钟数,可以是所有整数
这段代码说明在任务运行到14:08-14:09时间段内,任务就会停止。
注意:为什么停止的时间是一个时间段呢,因为我们每次运行1000条,每次1000条运行完了我们才会进行判断当前时间是否满足了停止的条件,我们没有必要迁移一条数据就去判断,没这个必要。