在上一节中,我们已经成功的实现了动态配置起始时间,那么现在我们来实现一个简单的cron,那么我们定义一个三个参数的cron
分别是分钟 小时 和周天 主要是增加一个init()的方法,为了简单起见,分钟只实现了 / 其他的只实现了 - 的功能
/** */
public ScheduledTask(String taskId, String className, String method,String cron){
this.taskId = taskId;
this.className = className;
this.method = method;
this.cron = cron;
/**
* 分钟 小时 天/月 天/周
* 分钟 小时 天/周
* *:表示匹配该域的任意值,假如在Minutes域使用*, 即表示每分钟都会触发事件
* ?:只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和 DayofWeek会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用?,而不能使用*,如果使用*表示不管星期几都会触发,实际上并不是这样。
* -:表示范围,例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次
* /:表示起始时间开始触发,然后每隔固定时间触发一次,例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次
**/
init();
runnable = () -> {
Object bean = SpringContextUtil.getBean(className);
try {
Method method1 = bean.getClass().getMethod(method);
method1.invoke(bean,null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
};
}
private void init() {
String cron = this.cron;
// 用空格分离 0/5 9-18 1-5
String[] splitCron = cron.split(" ");
String mintues = splitCron[0];
String hours = splitCron[1];
String dayOfWeek = splitCron[2];
// 看分钟 确定间隔时间
if(mintues.indexOf("/") > 0){
String delayStr = mintues.substring(mintues.indexOf("/")+1);
this.delay = Integer.parseInt(delayStr) * 1000;
this.initialDelay = Integer.parseInt(delayStr) * 1000;
}
// 看小时 确定开始结束时间
if(hours.indexOf("-") > 0){
String[] hourArray = hours.split("-");
this.beginTime = hourArray[0] + ":00";
this.endTime = hourArray[1] + ":00";
}
// 看天 确定开始结束日期
if(dayOfWeek.indexOf("-") > 0){
String[] dayArray = dayOfWeek.split("-");
this.beginWeekDay = Integer.parseInt(dayArray[0]);
this.endWeekDay = Integer.parseInt(dayArray[1]);
}
}
那个整个ScheduledTask的代码如下:
package com.bxlsj.schedule;
import com.bxlsj.util.SpringContextUtil;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
/**
* @author Eric
* @version 1.0
* @ClassName: ScheduledTask
* @Description: 定时任务类
* @company lsj
* @date 2019/3/13 11:10
**/
public class ScheduledTask {
/** 需要执行方法的线程 */
private Runnable runnable;
/** 唯一的id用于增删改 */
private String taskId;
/** 定时任务需要执行的方法类 */
private String className;
/** 定时任务需要执行的方法 */
private String method;
/** 首次执行等待时间 */
private long initialDelay;
/** 间隔时间 */
private long delay;
/** 开始时间 12:00*/
private String beginTime;
/** 结束时间 14:00*/
private String endTime;
private Integer beginWeekDay;
private Integer endWeekDay;
private Integer beginMonthDay;
private Integer endMonthday;
private Integer beginMonth;
private Integer endMonth;
private String cron;
public ScheduledTask(){
}
public ScheduledTask(String taskId, String className, String method,long initialDelay,long delay) {
this.taskId = taskId;
this.className = className;
this.method = method;
this.initialDelay = initialDelay;
this.delay = delay;
runnable = () -> {
Object bean = SpringContextUtil.getBean(className);
try {
Method method1 = bean.getClass().getMethod(method);
method1.invoke(bean,null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
};
}
public ScheduledTask(String taskId, String className, String method,long initialDelay,long delay,String beginTime,String endTime) {
this.taskId = taskId;
this.className = className;
this.method = method;
this.initialDelay = initialDelay;
this.delay = delay;
this.beginTime = beginTime;
this.endTime = endTime;
runnable = () -> {
Object bean = SpringContextUtil.getBean(className);
try {
Method method1 = bean.getClass().getMethod(method);
method1.invoke(bean,null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
};
}
/** */
public ScheduledTask(String taskId, String className, String method,String cron){
this.taskId = taskId;
this.className = className;
this.method = method;
this.cron = cron;
/**
* 分钟 小时 天/月 天/周
* 分钟 小时 天/周
* *:表示匹配该域的任意值,假如在Minutes域使用*, 即表示每分钟都会触发事件
* ?:只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和 DayofWeek会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用?,而不能使用*,如果使用*表示不管星期几都会触发,实际上并不是这样。
* -:表示范围,例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次
* /:表示起始时间开始触发,然后每隔固定时间触发一次,例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次
**/
init();
runnable = () -> {
Object bean = SpringContextUtil.getBean(className);
try {
Method method1 = bean.getClass().getMethod(method);
method1.invoke(bean,null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
};
}
private void init() {
String cron = this.cron;
// 用空格分离 0/5 9-18 1-5
String[] splitCron = cron.split(" ");
String mintues = splitCron[0];
String hours = splitCron[1];
String dayOfWeek = splitCron[2];
// 看分钟 确定间隔时间
if(mintues.indexOf("/") > 0){
String delayStr = mintues.substring(mintues.indexOf("/")+1);
this.delay = Integer.parseInt(delayStr) * 1000;
this.initialDelay = Integer.parseInt(delayStr) * 1000;
}
// 看小时 确定开始结束时间
if(hours.indexOf("-") > 0){
String[] hourArray = hours.split("-");
this.beginTime = hourArray[0] + ":00";
this.endTime = hourArray[1] + ":00";
}
// 看天 确定开始结束日期
if(dayOfWeek.indexOf("-") > 0){
String[] dayArray = dayOfWeek.split("-");
this.beginWeekDay = Integer.parseInt(dayArray[0]);
this.endWeekDay = Integer.parseInt(dayArray[1]);
}
}
public Runnable getRunnable() {
return runnable;
}
public void setRunnable(Runnable runnable) {
this.runnable = runnable;
}
public String getTaskId() {
return taskId;
}
public void setTaskId(String taskId) {
this.taskId = taskId;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public long getInitialDelay() {
return initialDelay;
}
public void setInitialDelay(long initialDelay) {
this.initialDelay = initialDelay;
}
public long getDelay() {
return delay;
}
public void setDelay(long delay) {
this.delay = delay;
}
public String getBeginTime() {
return beginTime;
}
public void setBeginTime(String beginTime) {
this.beginTime = beginTime;
}
public String getEndTime() {
return endTime;
}
public void setEndTime(String endTime) {
this.endTime = endTime;
}
public Integer getBeginWeekDay() {
return beginWeekDay;
}
public void setBeginWeekDay(Integer beginWeekDay) {
this.beginWeekDay = beginWeekDay;
}
public Integer getEndWeekDay() {
return endWeekDay;
}
public void setEndWeekDay(Integer endWeekDay) {
this.endWeekDay = endWeekDay;
}
public Integer getBeginMonthDay() {
return beginMonthDay;
}
public void setBeginMonthDay(Integer beginMonthDay) {
this.beginMonthDay = beginMonthDay;
}
public Integer getEndMonthday() {
return endMonthday;
}
public void setEndMonthday(Integer endMonthday) {
this.endMonthday = endMonthday;
}
public Integer getBeginMonth() {
return beginMonth;
}
public void setBeginMonth(Integer beginMonth) {
this.beginMonth = beginMonth;
}
public Integer getEndMonth() {
return endMonth;
}
public void setEndMonth(Integer endMonth) {
this.endMonth = endMonth;
}
public String getCron() {
return cron;
}
public void setCron(String cron) {
this.cron = cron;
}
}
好,我们来改造 ScheduledServiceImpl 类,主要是修改 StartTask 线程类的内容
private class StartTask implements Runnable {
@Override
public void run() {
System.out.println("StartTask");
// 循环遍历
try {
if (waitForStart.size() > 0) {
Set> entries = waitForStart.entrySet();
Iterator> iterator = entries.iterator();
while (iterator.hasNext()) {
// 取第一条数据
ScheduledTask frist = iterator.next().getValue();
// 检查星期几
LocalDate currentDate = LocalDate.now();
Integer weekDay = WEEK_MAP.get(String.valueOf(currentDate.getDayOfWeek()));
if((weekDay >= frist.getBeginWeekDay()) && (weekDay <= frist.getEndWeekDay())){
String format = DateUtil.format(new Date(), "yyyy-MM-dd");
Date endDate = DateUtil.gMT8StringToLocalDate(format + " " + frist.getEndTime(), "yyyy-MM-dd HH:mm");
Date startDate = DateUtil.gMT8StringToLocalDate(format + " " + frist.getBeginTime(), "yyyy-MM-dd HH:mm");
if (startDate.compareTo(new Date()) < 0 && endDate.compareTo(new Date()) > 0) {
// 开始方法
ScheduledFuture> scheduledFuture = service.scheduleWithFixedDelay(frist.getRunnable(), frist.getInitialDelay(), frist.getDelay(), TimeUnit.MILLISECONDS);
futureMap.put(frist.getTaskId(), scheduledFuture);
// 放入待关闭set中
System.out.println("StartTask 放入待关闭map");
waitForEnd.put(frist.getTaskId(), frist);
iterator.remove();
}
// 如果结束时间已经小于当前时间 表示本日不需要再执行 放入完成队列中
if (endDate.compareTo(new Date()) < 0) {
iterator.remove();
System.out.println("StartTask 放入待重置map");
completeSet.put(frist.getTaskId(), frist);
}
}else{
iterator.remove();
System.out.println("StartTask 放入待重置map");
completeSet.put(frist.getTaskId(), frist);
}
}
} else {
// System.out.println("StartTask" + waitForStart.size());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
整体如下:
package com.bxlsj.schedule;
import com.bxlsj.util.DateUtil;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
import javax.annotation.PreDestroy;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.*;
import static reactor.rx.stream.MapStream.Operation.put;
/**
* @author Eric
* @version 1.0
* @ClassName: ScheduledServiceImpl
* @Description: TODO
* @company lsj
* @date 2019/3/12 16:15
**/
@Service
public class ScheduledServiceImpl implements ScheduledService, InitializingBean {
private static ScheduledExecutorService service;
private static Map futureMap = new ConcurrentHashMap<>();
private Map waitForStart = new ConcurrentHashMap<>();
private Map waitForEnd = new ConcurrentHashMap<>();
private Map completeSet = new ConcurrentHashMap<>();
/**
Monday Mon 周一
Tuesday Tue 周二
Wednesday Wed 周三
Thursday Thu 周四
Friday Fri 周五
Saturday Sat 周六
Sunday
**/
static Map WEEK_MAP = new HashMap() {
{
put("MONDAY", 1);
put("TUESDAY", 2);
put("WEDNESDAY", 3);
put("THURSDAY", 4);
put("FRIDAY", 5);
put("SATURDAY", 6);
put("SUNDAY", 7);
}
};
@Override
public boolean addTask(ScheduledTask scheduledTask) {
if (futureMap.get(scheduledTask.getTaskId()) != null) {
return false;
}
//
// ScheduledFuture> scheduledFuture = service.scheduleWithFixedDelay(scheduledTask.getRunnable()
// , scheduledTask.getInitialDelay(), scheduledTask.getDelay(), TimeUnit.MILLISECONDS);
//
// futureMap.put(scheduledTask.getTaskId(),scheduledFuture);
waitForStart.put(scheduledTask.getTaskId(), scheduledTask);
return true;
}
@Override
public boolean updateTask(ScheduledTask scheduledTask) throws InterruptedException {
if (futureMap.get(scheduledTask.getTaskId()) == null) {
return false;
}
// 先停止
remove(scheduledTask.getTaskId());
// 再添加
addTask(scheduledTask);
return true;
}
@Override
public boolean remove(String taskId) {
if (futureMap.get(taskId) == null) {
return false;
}
ScheduledFuture scheduledFuture = futureMap.get(taskId);
scheduledFuture.cancel(false);
futureMap.remove(taskId);
return true;
}
@Override
public void shutdown() {
service.shutdown();
}
@Override
public void init() throws Exception {
afterPropertiesSet();
}
@Override
public void afterPropertiesSet() throws Exception {
// 初始化
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("schedule-pool-%d").setDaemon(true).build();
if (service != null) {
service.shutdown();
}
service = new ScheduledThreadPoolExecutor(1, threadFactory, new ThreadPoolExecutor.AbortPolicy());
// 检查所有的任务类 并装入
// addTask(new ScheduledTask("3", "scheduledTestService", "test1"
// , 5000, 1000
// , "14:15", "14:35"));
//
// addTask(new ScheduledTask("4", "scheduledTestService", "test2"
// , 5000, 3000
// , "14:34", "14:40"));
addTask(new ScheduledTask("3", "scheduledTestService", "test1"
, "0/2 14-15 1-4"));
addTask(new ScheduledTask("4", "scheduledTestService", "test2"
, "0/1 8-14 1-4"));
addTask(new ScheduledTask("5", "scheduledTestService", "test2"
, "0/1 6-20 4-5"));
// 启动检测关闭线程
service.scheduleWithFixedDelay(new EndTask()
, 1000, 5000, TimeUnit.MILLISECONDS);
// 启动检测开始线程
service.scheduleWithFixedDelay(new StartTask(), 1000, 5000, TimeUnit.MILLISECONDS);
LocalDateTime midnight = LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
long millSeconds = ChronoUnit.MILLIS.between(LocalDateTime.now(), midnight);
System.out.println("当天还剩的毫秒数" + millSeconds);
service.scheduleWithFixedDelay(new initDaliy(), millSeconds, 24*60*60*1000, TimeUnit.MILLISECONDS);
}
@PreDestroy
public void destroy() {
System.out.println("进入销毁程序");
// if(service!=null){
// service.shutdown();
// }
}
private class EndTask implements Runnable {
@Override
public void run() {
// 取出TreeSet的第一条
if (waitForEnd.size() > 0) {
Set> entries = waitForEnd.entrySet();
Iterator> iterator = entries.iterator();
while (iterator.hasNext()) {
// 取第一条数据
ScheduledTask frist = iterator.next().getValue();
String format = DateUtil.format(new Date(), "yyyy-MM-dd");
Date date = DateUtil.gMT8StringToLocalDate(format + " " + frist.getEndTime(), "yyyy-MM-dd HH:mm");
if (date.compareTo(new Date()) < 0) {
// 停止这个方法
remove(frist.getTaskId());
// 放入完成set中
iterator.remove();
System.out.println("EndTask 放入待重置map");
completeSet.put(frist.getTaskId(), frist);
}
}
}
}
}
private class StartTask implements Runnable {
@Override
public void run() {
System.out.println("StartTask");
// 循环遍历
try {
if (waitForStart.size() > 0) {
Set> entries = waitForStart.entrySet();
Iterator> iterator = entries.iterator();
while (iterator.hasNext()) {
// 取第一条数据
ScheduledTask frist = iterator.next().getValue();
// 检查星期几
LocalDate currentDate = LocalDate.now();
Integer weekDay = WEEK_MAP.get(String.valueOf(currentDate.getDayOfWeek()));
if((weekDay >= frist.getBeginWeekDay()) && (weekDay <= frist.getEndWeekDay())){
String format = DateUtil.format(new Date(), "yyyy-MM-dd");
Date endDate = DateUtil.gMT8StringToLocalDate(format + " " + frist.getEndTime(), "yyyy-MM-dd HH:mm");
Date startDate = DateUtil.gMT8StringToLocalDate(format + " " + frist.getBeginTime(), "yyyy-MM-dd HH:mm");
if (startDate.compareTo(new Date()) < 0 && endDate.compareTo(new Date()) > 0) {
// 开始方法
ScheduledFuture> scheduledFuture = service.scheduleWithFixedDelay(frist.getRunnable(), frist.getInitialDelay(), frist.getDelay(), TimeUnit.MILLISECONDS);
futureMap.put(frist.getTaskId(), scheduledFuture);
// 放入待关闭set中
System.out.println("StartTask 放入待关闭map");
waitForEnd.put(frist.getTaskId(), frist);
iterator.remove();
}
// 如果结束时间已经小于当前时间 表示本日不需要再执行 放入完成队列中
if (endDate.compareTo(new Date()) < 0) {
iterator.remove();
System.out.println("StartTask 放入待重置map");
completeSet.put(frist.getTaskId(), frist);
}
}else{
iterator.remove();
System.out.println("StartTask 放入待重置map");
completeSet.put(frist.getTaskId(), frist);
}
}
} else {
// System.out.println("StartTask" + waitForStart.size());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 每天夜里24点执行 将已完成队列放入准备开始队列中
*
* @author Eric
* @date 15:31 2019/3/13
* @params null
* @throws
* @return
**/
private class initDaliy implements Runnable {
@Override
public void run() {
waitForStart.putAll(completeSet);
// System.out.println("waitForStart" + waitForStart.size());
// System.out.println(waitForEnd.size());
// System.out.println("completeSet" + completeSet.size());
}
}
}
好,这样就改造完成了,可以实现比如 周一到周五 每天早8点到晚6点 每隔10分钟执行一次的功能
运行结果就不打印了,下一章节我们来试着实现分布式部署的情况下定时任务如何进行同步