From 03eabb1b81c5d7e4ae0d1b171dd4b44e30844ae5 Mon Sep 17 00:00:00 2001 From: lxyer <237809796@qq.com> Date: Sun, 5 Aug 2018 21:04:30 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=8A=BD=E5=8F=96=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/lxyer/timer/AbstractTask.java | 36 ++ src/main/java/com/lxyer/timer/Task.java | 495 +----------------- .../java/com/lxyer/timer/TimerExecutor.java | 91 +--- src/main/java/com/lxyer/timer/TimerQueue.java | 80 +++ .../com/lxyer/timer/scheduled/Scheduled.java | 21 + .../lxyer/timer/scheduled/ScheduledCycle.java | 35 ++ .../timer/scheduled/ScheduledExpres.java | 483 +++++++++++++++++ src/test/java/com/lxyer/timer/TaskImpl.java | 34 ++ src/test/java/com/lxyer/timer/TimerTest.java | 48 +- 9 files changed, 705 insertions(+), 618 deletions(-) create mode 100644 src/main/java/com/lxyer/timer/AbstractTask.java create mode 100644 src/main/java/com/lxyer/timer/TimerQueue.java create mode 100644 src/main/java/com/lxyer/timer/scheduled/Scheduled.java create mode 100644 src/main/java/com/lxyer/timer/scheduled/ScheduledCycle.java create mode 100644 src/main/java/com/lxyer/timer/scheduled/ScheduledExpres.java create mode 100644 src/test/java/com/lxyer/timer/TaskImpl.java diff --git a/src/main/java/com/lxyer/timer/AbstractTask.java b/src/main/java/com/lxyer/timer/AbstractTask.java new file mode 100644 index 0000000..b35fbe7 --- /dev/null +++ b/src/main/java/com/lxyer/timer/AbstractTask.java @@ -0,0 +1,36 @@ +package com.lxyer.timer; + +import com.lxyer.timer.scheduled.Scheduled; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +/** + * Created by liangxianyou at 2018/7/23 14:33. + */ +public abstract class AbstractTask implements Task { + protected String name; + private long theTime; + private Scheduled scheduled; + + public AbstractTask(String name, Scheduled scheduled) { + this.name = name; + this.scheduled = scheduled; + this.theTime = scheduled.theTime().toInstant(ZoneOffset.of("+8")).toEpochMilli(); + } + + @Override + public long nextTime(){ + LocalDateTime next = scheduled.nextTime(); + this.theTime = next.toInstant(ZoneOffset.of("+8")).toEpochMilli(); + + /*SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + System.out.println("下次执行:"+ sdf.format(next.toInstant(ZoneOffset.of("+8")).toEpochMilli()));*/ + return theTime; + } + @Override + public long theTime(){ + return theTime; + } +} + diff --git a/src/main/java/com/lxyer/timer/Task.java b/src/main/java/com/lxyer/timer/Task.java index 449b592..fb627df 100644 --- a/src/main/java/com/lxyer/timer/Task.java +++ b/src/main/java/com/lxyer/timer/Task.java @@ -1,497 +1,10 @@ package com.lxyer.timer; -import java.text.SimpleDateFormat; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.ZoneOffset; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - /** - * Created by liangxianyou at 2018/7/23 14:33. + * @author: liangxianyou at 2018/8/5 19:32. */ -public abstract class Task implements Runnable { - protected String name; - protected long nextTime; - private TimerScheduled timerScheduled; - public abstract void run(); - - public Task(String name, String cfg){ - this.name = name; - LocalDateTime now = LocalDateTime.now(); - timerScheduled = new TimerScheduled(now.getYear(), now.getMonthValue(), cfg); - nextTime = timerScheduled.nextTime.toInstant(ZoneOffset.of("+8")).toEpochMilli(); - } - - public long nextTime(){ - LocalDateTime next = timerScheduled.nextTime(); - this.nextTime = next.toInstant(ZoneOffset.of("+8")).toEpochMilli(); - return nextTime; - } -} - -/** - * 时间解析器 - */ -@SuppressWarnings("Duplicates") -class TimerScheduled { - private int year; - private int month; - private int[] minutes; - private int[] hours; - private int[] days; - private int[] monthes; - private int[] weeks; - String[] cfgArr; - - LocalDateTime nextTime; - private int _y,_M,_d,_H,_m; - - public TimerScheduled(int year, int month, String cfg) { - this.year = year; - this.month = month; - this.cfgArr = cfg.split(" "); - - setWeeks(); - setMonthes(); - setDays(); - setHours(); - setMinutes(); - - //寻找初始合法时间 - LocalDateTime now = LocalDateTime.now(); - _y = now.getYear(); - _M= now.getMonthValue(); - _d= now.getDayOfMonth(); - _H= now.getHour(); - _m= now.getMinute(); - - String cmd = "";//y M d H m - if (days.length == 0) cmd = "M"; - do { - carry(cmd); - int inx; - if ((inx = nowOk(monthes, _M)) < 0){ - cmd = "y"; - continue; - } - _M = monthes[inx]; - - if ((inx = nowOk(days, _d)) < 0){ - cmd = "M"; - continue; - } - _d = days[inx]; - - if ((inx = nowOk(hours, _H)) < 0){ - cmd = "d"; - continue; - } - _H = hours[inx]; - - if ((inx = nowOk(minutes, _m)) < 0){ - cmd = "H"; - continue; - } - _m = minutes[inx]; - break; - }while (true); - - if (_y == now.getYear() && _M == now.getMonthValue() && _d == now.getDayOfMonth() - && _H == now.getHour() && _m == now.getMinute()){ - carry("m"); - } - - nextTime = LocalDateTime.of(_y, _M, _d, _H, _m); - System.out.println(cfg); - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - System.out.println("init:"+ sdf.format(nextTime.toInstant(ZoneOffset.of("+8")).toEpochMilli())); - } - - /** - * 下一次执行的时间 - * @return - */ - protected LocalDateTime nextTime(){ - - String cmd = "m"; - carry(cmd); - nextTime = LocalDateTime.of(year, month - , _d == -1? nextTime.getDayOfMonth() : _d - , _H == -1? nextTime.getHour() : _H - , _m == -1? nextTime.getMinute() : _m); - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - System.out.println("下次执行:"+ sdf.format(nextTime.toInstant(ZoneOffset.of("+8")).toEpochMilli())); - return nextTime; - } - - /** - * 通过发送指令进行进位 - * @param cmd 进位指令 - */ - private void carry(String cmd){ - int inx; - while (!"".equals(cmd)){ - switch (cmd) { - case "y": - _y = this.year = ++_y; - _M = this.month = monthes[0]; - setDays(); - if (days.length == 0){ - cmd = "M"; - continue; - } - _d = days[0]; - _H = hours[0]; - _m = minutes[0]; - break; - case "M": - if (_M < monthes[0]){ - _M = monthes[0]; - break; - } - inx = Arrays.binarySearch(monthes, _M); - if (inx < 0 || inx >= monthes.length-1) { - cmd = "y"; - continue ; - } - _M = this.month = monthes[inx+1]; - setDays(); - if (days.length == 0){ - cmd = "M"; - continue; - } - _d = days[0]; - _H = hours[0]; - _m = minutes[0]; - break; - case "d": - if (_d < days[0]){ - _d = days[0]; - break; - } - inx = Arrays.binarySearch(days, _d); - if (inx < 0 || inx >= days.length-1) { - cmd = "M"; - continue ; - } - _d = days[inx+1]; - _H = hours[0]; - _m = minutes[0]; - break; - case "H": - if (_H < hours[0]){ - _H = hours[0]; - break; - } - inx = Arrays.binarySearch(hours, _H); - if (inx < 0 || inx >= hours.length -1) { - cmd = "d"; - continue ; - } - _H = hours[inx+1]; - _m = minutes[0]; - break; - case "m": - if (_m < minutes[0]){ - _m = minutes[0]; - break; - } - inx = Arrays.binarySearch(minutes, _m); - if (inx < 0 || inx >= minutes.length -1) { - cmd = "H"; - continue ; - } - _m = minutes[inx+1]; - break; - } - cmd = ""; - } - } - - /** - * 得到初始合法时间的索引 - * @param arr 合法时间序列 - * @param n 初始选中值 - * @return 合法时间的索引 - */ - private int nowOk(int[] arr, int n){ - if (arr == null || arr.length == 0) return -1; - if (arr[0] > n) - return 0; - if (arr[arr.length-1] < n) - return -1; - if (arr[arr.length-1] == n) - return arr.length-1; - - for (int i = 0; i < arr.length-1; i++) { - if ((arr[i] < n && arr[i+1] > n) || arr[i] == n){ - return i; - } - } - - return -1; - } - - /** 以下为 初始化合法时间 weeks,monthes,days,hour,minutes 序列 */ - private void setMinutes(){ - String cfg = cfgArr[0]; - if ("*".equals(cfg)){//* - minutes = new int[60]; - for (int i = 0; i < 60; i++) { - minutes[i] = i; - } - } else if (cfg.matches("^[0-5]??[0-9]??$")){//n - minutes = new int[1]; - minutes[0] = Integer.parseInt(cfg); - }else if (cfg.matches("^[*]/[0-9]+$")){// */5 - String[] strArr = cfg.split("/"); - int p = Integer.parseInt(strArr[1]); - minutes = new int[60/p]; - for (int i = 0; i < minutes.length; i++) { - minutes[i] = i*p; - } - }else if (cfg.matches("^([0-5]??[0-9]??,)+([0-5]??[0-9]??)?$")){//1,3 - String[] strings = cfg.split(","); - minutes = new int[strings.length]; - for (int i = 0; i < strings.length; i++) { - minutes[i] = Integer.parseInt(strings[i]); - } - }else if (cfg.matches("^[0-5]??[0-9]??\\-[0-5]??[0-9]??$")){//1-3 - String[] split = cfg.split("-"); - int s = Integer.parseInt(split[0]); - int e = Integer.parseInt(split[1]); - - minutes = new int[e-s +1]; - for (int i = 0; i < minutes.length; i++) { - minutes[i] = s+i; - } - }else if (cfg.matches("^[0-5]??[0-9]??\\-[0-5]??[0-9]??/[0-5]??[0-9]??$")){//3-18/5 - String[] strArr = cfg.split("/"); - String[] str2Arr = strArr[0].split("-"); - int s = Integer.parseInt(str2Arr[0]); - int e = Integer.parseInt(str2Arr[1]); - int p = Integer.parseInt(strArr[1]); - - minutes = new int[(e-s)/p]; - for (int i = 0; i < minutes.length; i++) { - minutes[i] = s + i*p; - } - } - } - - private void setHours(){ - String cfg = cfgArr[1]; - if ("*".equals(cfg)){//* - hours = new int[24]; - for (int i = 0; i < hours.length; i++) { - hours[i] = i; - } - } else if (cfg.matches("^[0-5]??[0-9]??$")){//n - hours = new int[1]; - hours[0] = Integer.parseInt(cfg); - }else if (cfg.matches("^[*]/[0-9]+$")){// */5 - String[] strArr = cfg.split("/"); - int p = Integer.parseInt(strArr[1]); - hours = new int[24/p]; - for (int i = 0; i < hours.length; i++) { - hours[i] = i*p; - } - }else if (cfg.matches("^([0-5]??[0-9]??,)+([0-5]??[0-9]??)?$")){//1,3 - String[] strArr = cfg.split(","); - hours = new int[strArr.length]; - for (int i = 0; i < strArr.length; i++) { - hours[i] = Integer.parseInt(strArr[i]); - } - }else if (cfg.matches("^[0-5]??[0-9]??\\-[0-5]??[0-9]??$")){//1-3 - String[] split = cfg.split("-"); - int s = Integer.parseInt(split[0]); - int e = Integer.parseInt(split[1]); - - hours = new int[e-s +1]; - for (int i = 0; i < hours.length; i++) { - hours[i] = s+i; - } - }else if (cfg.matches("^[0-5]??[0-9]??\\-[0-5]??[0-9]??/[0-5]??[0-9]??$")){//3-18/5 - String[] strArr = cfg.split("/"); - String[] str2Arr = strArr[0].split("-"); - int s = Integer.parseInt(str2Arr[0]); - int e = Integer.parseInt(str2Arr[1]); - int p = Integer.parseInt(strArr[1]); - - hours = new int[(e-s)/p]; - for (int i = 0; i < hours.length; i++) { - hours[i] = s + i*p; - } - } - } - - private void setWeeks(){ - String cfg = cfgArr[4]; - if ("*".equals(cfg)){//* - weeks = new int[7]; - for (int i = 0; i < weeks.length; i++) { - weeks[i] = i+1; - } - } else if (cfg.matches("^[0-5]??[0-9]??$")){//n - weeks = new int[1]; - weeks[0] = Integer.parseInt(cfg); - }else if (cfg.matches("^[*]/[0-9]+$")){// */5 - String[] strArr = cfg.split("/"); - int p = Integer.parseInt(strArr[1]); - weeks = new int[7/p]; - for (int i = 0; i < weeks.length; i++) { - weeks[i] = i*p; - } - }else if (cfg.matches("^([0-5]??[0-9]??,)+([0-5]??[0-9]??)?$")){//1,3 - String[] strArr = cfg.split(","); - weeks = new int[strArr.length]; - for (int i = 0; i < strArr.length; i++) { - weeks[i] = Integer.parseInt(strArr[i]); - } - }else if (cfg.matches("^[0-5]??[0-9]??\\-[0-5]??[0-9]??$")){//1-3 - String[] split = cfg.split("-"); - int s = Integer.parseInt(split[0]); - int e = Integer.parseInt(split[1]); - - weeks = new int[e-s +1]; - for (int i = 0; i < weeks.length; i++) { - weeks[i] = s+i; - } - }else if (cfg.matches("^[0-5]??[0-9]??\\-[0-5]??[0-9]??/[0-5]??[0-9]??$")){//3-18/5 - String[] strArr = cfg.split("/"); - String[] str2Arr = strArr[0].split("-"); - int s = Integer.parseInt(str2Arr[0]); - int e = Integer.parseInt(str2Arr[1]); - int p = Integer.parseInt(strArr[1]); - - weeks = new int[(e-s)/p]; - for (int i = 0; i < weeks.length; i++) { - weeks[i] = s + i*p; - } - } - } - - private void setMonthes(){ - String cfg = cfgArr[3]; - if ("*".equals(cfg)){//* - monthes = new int[12]; - for (int i = 0; i < monthes.length; i++) { - monthes[i] = i+1; - } - } else if (cfg.matches("^[0-5]??[0-9]??$")){//n - monthes = new int[1]; - monthes[0] = Integer.parseInt(cfg); - }else if (cfg.matches("^[*]/[0-9]+$")){// */5 - String[] strArr = cfg.split("/"); - int p = Integer.parseInt(strArr[1]); - monthes = new int[12/p]; - for (int i = 0; i < monthes.length; i++) { - monthes[i] = i*p; - } - }else if (cfg.matches("^([0-5]??[0-9]??,)+([0-5]??[0-9]??)?$")){//1,3 - String[] strArr = cfg.split(","); - monthes = new int[strArr.length]; - for (int i = 0; i < strArr.length; i++) { - monthes[i] = Integer.parseInt(strArr[i]); - } - }else if (cfg.matches("^[0-5]??[0-9]??\\-[0-5]??[0-9]??$")){//1-3 - String[] split = cfg.split("-"); - int s = Integer.parseInt(split[0]); - int e = Integer.parseInt(split[1]); - - monthes = new int[e-s +1]; - for (int i = 0; i < monthes.length; i++) { - monthes[i] = s+i; - } - }else if (cfg.matches("^[0-5]??[0-9]??\\-[0-5]??[0-9]??/[0-5]??[0-9]??$")){//3-18/5 - String[] strArr = cfg.split("/"); - String[] str2Arr = strArr[0].split("-"); - int s = Integer.parseInt(str2Arr[0]); - int e = Integer.parseInt(str2Arr[1]); - int p = Integer.parseInt(strArr[1]); - - monthes = new int[(e-s)/p]; - for (int i = 0; i < monthes.length; i++) { - monthes[i] = s + i*p; - } - } - } - - private void setDays(){ - String cfg = cfgArr[2]; - //当前月份总天数, - LocalDate firstDay = LocalDate.of(year, month, 1); - int lengthOfMonth = firstDay.lengthOfMonth(); - - if ("*".equals(cfg)){//* - days = new int[lengthOfMonth]; - for (int i = 0; i < days.length; i++) { - days[i] = i+1; - } - } else if (cfg.matches("^[0-5]??[0-9]??$")){//n - days = new int[1]; - days[0] = Integer.parseInt(cfg); - }else if (cfg.matches("^[*]/[0-9]+$")){// */5 - String[] strArr = cfg.split("/"); - int p = Integer.parseInt(strArr[1]); - days = new int[lengthOfMonth/p]; - for (int i = 0; i < days.length; i++) { - days[i] = i*p; - } - }else if (cfg.matches("^([0-5]??[0-9]??,)+([0-5]??[0-9]??)?$")){//1,3 - String[] strArr = cfg.split(","); - days = new int[strArr.length]; - for (int i = 0; i < strArr.length; i++) { - days[i] = Integer.parseInt(strArr[i]); - } - }else if (cfg.matches("^[0-5]??[0-9]??\\-[0-5]??[0-9]??$")){//1-3 - String[] split = cfg.split("-"); - int s = Integer.parseInt(split[0]); - int e = Integer.parseInt(split[1]); - - days = new int[e-s +1]; - for (int i = 0; i < days.length; i++) { - days[i] = s+i; - } - }else if (cfg.matches("^[0-5]??[0-9]??\\-[0-5]??[0-9]??/[0-5]??[0-9]??$")){//3-18/5 - String[] strArr = cfg.split("/"); - String[] str2Arr = strArr[0].split("-"); - int s = Integer.parseInt(str2Arr[0]); - int e = Integer.parseInt(str2Arr[1]); - int p = Integer.parseInt(strArr[1]); - - days = new int[(e-s)/p]; - for (int i = 0; i < days.length; i++) { - days[i] = s + i*p; - } - } - - int firstWeek = firstDay.getDayOfWeek().getValue(); - List allDay = new ArrayList<>(); - for (int i = 0; i < days.length; i++) { - //int week = 7 - Math.abs(i - firstWeek) % 7;//当前星期X - int week; - int d = days[i]; - if (d + firstWeek <= 8){ - week = firstWeek + d - 1; - }else { - week = (d - (8-firstWeek))%7; - if (week == 0) week = 7; - } - - //System.out.printf("M:%s,d:%s,w:%s%n", month, d, week); - - if (Arrays.binarySearch(weeks, week) > -1){ - allDay.add(d);//加入日期 - } - } - - days = new int[allDay.size()]; - for (int i = 0; i < allDay.size(); i++) { - days[i] = allDay.get(i); - } - } +public interface Task extends Runnable{ + long nextTime(); + long theTime(); } diff --git a/src/main/java/com/lxyer/timer/TimerExecutor.java b/src/main/java/com/lxyer/timer/TimerExecutor.java index 7db4ee4..085574c 100644 --- a/src/main/java/com/lxyer/timer/TimerExecutor.java +++ b/src/main/java/com/lxyer/timer/TimerExecutor.java @@ -1,42 +1,33 @@ package com.lxyer.timer; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** - * Created by liangxianyou at 2018/7/23 14:07. + * @author: liangxianyou */ public class TimerExecutor { private TimerQueue queue = new TimerQueue(); private ExecutorService executor; - /** - * 创建定时调度器 - * @param n 调度线程数 - */ public TimerExecutor(int n) { executor = Executors.newFixedThreadPool(n); } - public void add(Task task){ + public void add(AbstractTask task){ queue.put(task); } - private void add(Task task, boolean upTime){ + private void add(AbstractTask task, boolean upTime){ if (upTime) task.nextTime(); queue.put(task); } - /** - * 启动定时调度 - */ + public void start() { new Thread(()->{ while (true){ try{ - Task take = null; + AbstractTask take = null; try { take = queue.take(); } catch (InterruptedException e) { @@ -51,76 +42,4 @@ public class TimerExecutor { } }).start(); } -} - -class TimerQueue{ - Object lock = new Object(); - Task[] queue = new Task[128]; - Set names = new HashSet<>(); - int size=0; - - void put(Task task) { - remove(task.name); - synchronized (lock){ - int inx = size;//目标坐标 - while (inx > 0 && queue[inx-1].nextTime > task.nextTime ){ - inx--; - } - - if (queue.length == size+1) - queue = Arrays.copyOf(queue, size * 2); - - for (int i = size+1; i > inx; i--) { - queue[i] = queue[i-1]; - queue[i-1] = null; - } - queue[inx] = task; - - size++; - names.add(task.name); - lock.notify(); - } - } - - Task take() throws InterruptedException { - synchronized (lock){ - if (size == 0) lock.wait(); - - long currentTime = System.currentTimeMillis(); - long nextTime = queue[0].nextTime; - - if (currentTime >= nextTime){ - Task task = queue[0]; - for (int i = 0; i < size;i++) { - queue[i] = queue[i+1]; - } - queue[size-1] = null; - size--; - return task; - }else { - lock.wait(nextTime - currentTime); - return take(); - } - } - } - - Task remove(String name){ - synchronized (lock){ - if(!names.contains(name)) return null; - - Task take = null; - for (int i = 0; i < size-1; i++) { - if (name.equals(queue[i].name)){ - take = queue[i]; - while (i < size+1){ - queue[i] = queue[i+1]; - queue[i+1] = null; - } - names.remove(name); - break; - } - } - return take; - } - } } \ No newline at end of file diff --git a/src/main/java/com/lxyer/timer/TimerQueue.java b/src/main/java/com/lxyer/timer/TimerQueue.java new file mode 100644 index 0000000..11ba83d --- /dev/null +++ b/src/main/java/com/lxyer/timer/TimerQueue.java @@ -0,0 +1,80 @@ +package com.lxyer.timer; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * Created by liangxianyou at 2018/7/23 14:07. + */ +class TimerQueue{ + Object lock = new Object(); + AbstractTask[] queue = new AbstractTask[128]; + Set names = new HashSet<>(); + int size=0; + + void put(AbstractTask task) { + remove(task.name); + synchronized (lock){ + int inx = size;//目标坐标 + while (inx > 0 && queue[inx-1].theTime() > task.theTime()){ + inx--; + } + + if (queue.length == size+1) + queue = Arrays.copyOf(queue, size * 2); + + for (int i = size+1; i > inx; i--) { + queue[i] = queue[i-1]; + queue[i-1] = null; + } + queue[inx] = task; + + size++; + names.add(task.name); + lock.notify(); + } + } + + AbstractTask take() throws InterruptedException { + synchronized (lock){ + if (size == 0) lock.wait(); + + long currentTime = System.currentTimeMillis(); + long nextTime = queue[0].theTime(); + + if (currentTime >= nextTime){ + AbstractTask task = queue[0]; + for (int i = 0; i < size;i++) { + queue[i] = queue[i+1]; + } + queue[size-1] = null; + size--; + return task; + }else { + lock.wait(nextTime - currentTime); + return take(); + } + } + } + + AbstractTask remove(String name){ + synchronized (lock){ + if(!names.contains(name)) return null; + + AbstractTask take = null; + for (int i = 0; i < size-1; i++) { + if (name.equals(queue[i].name)){ + take = queue[i]; + while (i < size+1){ + queue[i] = queue[i+1]; + queue[i+1] = null; + } + names.remove(name); + break; + } + } + return take; + } + } +} diff --git a/src/main/java/com/lxyer/timer/scheduled/Scheduled.java b/src/main/java/com/lxyer/timer/scheduled/Scheduled.java new file mode 100644 index 0000000..84dc245 --- /dev/null +++ b/src/main/java/com/lxyer/timer/scheduled/Scheduled.java @@ -0,0 +1,21 @@ +package com.lxyer.timer.scheduled; + +import java.time.LocalDateTime; + +/** + * @author: liangxianyou at 2018/8/5 17:35. + */ +public interface Scheduled { + + /** + * 下次执行时间 + * @return + */ + LocalDateTime nextTime(); + + /** + * 当前执行时间 + * @return + */ + LocalDateTime theTime(); +} diff --git a/src/main/java/com/lxyer/timer/scheduled/ScheduledCycle.java b/src/main/java/com/lxyer/timer/scheduled/ScheduledCycle.java new file mode 100644 index 0000000..bb3bd4d --- /dev/null +++ b/src/main/java/com/lxyer/timer/scheduled/ScheduledCycle.java @@ -0,0 +1,35 @@ +package com.lxyer.timer.scheduled; + +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalUnit; + +/** + * @author: liangxianyou at 2018/8/5 18:05. + */ +public class ScheduledCycle implements Scheduled { + + private LocalDateTime theTime; + private long period; + private TemporalUnit unit = ChronoUnit.SECONDS; + + public ScheduledCycle(LocalDateTime startTime, long period) { + this.theTime = startTime; + this.period = period; + } + public ScheduledCycle(LocalDateTime startTime, long period, TemporalUnit unit) { + this.theTime = startTime; + this.period = period; + this.unit = unit; + } + + @Override + public LocalDateTime nextTime() { + return theTime.plus(period, unit); + } + + @Override + public LocalDateTime theTime() { + return theTime; + } +} diff --git a/src/main/java/com/lxyer/timer/scheduled/ScheduledExpres.java b/src/main/java/com/lxyer/timer/scheduled/ScheduledExpres.java new file mode 100644 index 0000000..38cf7d8 --- /dev/null +++ b/src/main/java/com/lxyer/timer/scheduled/ScheduledExpres.java @@ -0,0 +1,483 @@ +package com.lxyer.timer.scheduled; + +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 时间解析器 + * @author: liangxianyou + */ +@SuppressWarnings("Duplicates") +public class ScheduledExpres implements Scheduled{ + private int year; + private int month; + private int[] minutes; + private int[] hours; + private int[] days; + private int[] monthes; + private int[] weeks; + private String[] cfgArr; + + private LocalDateTime theTime; + private int _y,_M,_d,_H,_m; + + public ScheduledExpres(String cfg){ + this.theTime = LocalDateTime.now(); + initTheTime(cfg); + } + + public ScheduledExpres(final LocalDateTime startTime, String cfg){ + LocalDateTime now = LocalDateTime.now(); + this.theTime = now.isAfter(startTime)? now : startTime; + initTheTime(cfg); + } + + //寻找初始合法时间 + public void initTheTime( String cfg) { + year = theTime.getYear(); + month = theTime.getMonthValue(); + cfgArr = cfg.split(" "); + + setWeeks(); + setMonthes(); + setDays(); + setHours(); + setMinutes(); + + _y = theTime.getYear(); + _M= theTime.getMonthValue(); + _d= theTime.getDayOfMonth(); + _H= theTime.getHour(); + _m= theTime.getMinute(); + + String cmd = "";//y M d H m + if (days.length == 0) cmd = "M"; + do { + carry(cmd); + int inx; + if ((inx = nowOk(monthes, _M)) < 0){ + cmd = "y"; + continue; + } + _M = monthes[inx]; + + if ((inx = nowOk(days, _d)) < 0){ + cmd = "M"; + continue; + } + _d = days[inx]; + + if ((inx = nowOk(hours, _H)) < 0){ + cmd = "d"; + continue; + } + _H = hours[inx]; + + if ((inx = nowOk(minutes, _m)) < 0){ + cmd = "H"; + continue; + } + _m = minutes[inx]; + break; + }while (true); + + theTime = LocalDateTime.of(_y, _M, _d, _H, _m); + LocalDateTime now = LocalDateTime.now(); + while (theTime.isBefore(now)){ + theTime = carry("m"); + } + + System.out.println(cfg); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + System.out.println("init:"+ sdf.format(this.theTime.toInstant(ZoneOffset.of("+8")).toEpochMilli())); + } + + /** + * 下一次执行的时间 + * @return + */ + @Override + public LocalDateTime nextTime(){ + return carry("m"); + } + + @Override + public LocalDateTime theTime() { + return theTime; + } + + /** + * 通过发送指令进行进位 + * @param cmd 进位指令 + */ + private LocalDateTime carry(String cmd){ + int inx; + while (!"".equals(cmd)){ + switch (cmd) { + case "y": + _y = this.year = ++_y; + _M = this.month = monthes[0]; + setDays(); + if (days.length == 0){ + cmd = "M"; + continue; + } + _d = days[0]; + _H = hours[0]; + _m = minutes[0]; + break; + case "M": + if (_M < monthes[0]){ + _M = monthes[0]; + break; + } + inx = Arrays.binarySearch(monthes, _M); + if (inx < 0 || inx >= monthes.length-1) { + cmd = "y"; + continue ; + } + _M = this.month = monthes[inx+1]; + setDays(); + if (days.length == 0){ + cmd = "M"; + continue; + } + _d = days[0]; + _H = hours[0]; + _m = minutes[0]; + break; + case "d": + if (_d < days[0]){ + _d = days[0]; + break; + } + inx = Arrays.binarySearch(days, _d); + if (inx < 0 || inx >= days.length-1) { + cmd = "M"; + continue ; + } + _d = days[inx+1]; + _H = hours[0]; + _m = minutes[0]; + break; + case "H": + if (_H < hours[0]){ + _H = hours[0]; + break; + } + inx = Arrays.binarySearch(hours, _H); + if (inx < 0 || inx >= hours.length -1) { + cmd = "d"; + continue ; + } + _H = hours[inx+1]; + _m = minutes[0]; + break; + case "m": + if (_m < minutes[0]){ + _m = minutes[0]; + break; + } + inx = Arrays.binarySearch(minutes, _m); + if (inx < 0 || inx >= minutes.length -1) { + cmd = "H"; + continue ; + } + _m = minutes[inx+1]; + break; + } + cmd = ""; + } + return LocalDateTime.of(_y, _M, _d, _H, _m); + } + + /** + * 得到初始合法时间的索引 + * @param arr 合法时间序列 + * @param n 初始选中值 + * @return 合法时间的索引 + */ + private int nowOk(int[] arr, int n){ + if (arr == null || arr.length == 0) return -1; + if (arr[0] > n) + return 0; + if (arr[arr.length-1] < n) + return 0; + if (arr[arr.length-1] == n) + return arr.length-1; + + for (int i = 0; i < arr.length-1; i++) { + if ((arr[i] < n && arr[i+1] > n) || arr[i] == n){ + return i; + } + } + + return -1; + } + + /** 以下为 初始化合法时间 weeks,monthes,days,hour,minutes 序列 */ + private void setMinutes(){ + String cfg = cfgArr[0]; + if ("*".equals(cfg)){//* + minutes = new int[60]; + for (int i = 0; i < 60; i++) { + minutes[i] = i; + } + } else if (cfg.matches("^[0-5]??[0-9]??$")){//n + minutes = new int[1]; + minutes[0] = Integer.parseInt(cfg); + }else if (cfg.matches("^[*]/[0-9]+$")){// */5 + String[] strArr = cfg.split("/"); + int p = Integer.parseInt(strArr[1]); + minutes = new int[60/p]; + for (int i = 0; i < minutes.length; i++) { + minutes[i] = i*p; + } + }else if (cfg.matches("^([0-5]??[0-9]??,)+([0-5]??[0-9]??)?$")){//1,3 + String[] strings = cfg.split(","); + minutes = new int[strings.length]; + for (int i = 0; i < strings.length; i++) { + minutes[i] = Integer.parseInt(strings[i]); + } + }else if (cfg.matches("^[0-5]??[0-9]??\\-[0-5]??[0-9]??$")){//1-3 + String[] split = cfg.split("-"); + int s = Integer.parseInt(split[0]); + int e = Integer.parseInt(split[1]); + + minutes = new int[e-s +1]; + for (int i = 0; i < minutes.length; i++) { + minutes[i] = s+i; + } + }else if (cfg.matches("^[0-5]??[0-9]??\\-[0-5]??[0-9]??/[0-5]??[0-9]??$")){//3-18/5 + String[] strArr = cfg.split("/"); + String[] str2Arr = strArr[0].split("-"); + int s = Integer.parseInt(str2Arr[0]); + int e = Integer.parseInt(str2Arr[1]); + int p = Integer.parseInt(strArr[1]); + + minutes = new int[(e-s)/p]; + for (int i = 0; i < minutes.length; i++) { + minutes[i] = s + i*p; + } + } + } + + private void setHours(){ + String cfg = cfgArr[1]; + if ("*".equals(cfg)){//* + hours = new int[24]; + for (int i = 0; i < hours.length; i++) { + hours[i] = i; + } + } else if (cfg.matches("^[0-5]??[0-9]??$")){//n + hours = new int[1]; + hours[0] = Integer.parseInt(cfg); + }else if (cfg.matches("^[*]/[0-9]+$")){// */5 + String[] strArr = cfg.split("/"); + int p = Integer.parseInt(strArr[1]); + hours = new int[24/p]; + for (int i = 0; i < hours.length; i++) { + hours[i] = i*p; + } + }else if (cfg.matches("^([0-5]??[0-9]??,)+([0-5]??[0-9]??)?$")){//1,3 + String[] strArr = cfg.split(","); + hours = new int[strArr.length]; + for (int i = 0; i < strArr.length; i++) { + hours[i] = Integer.parseInt(strArr[i]); + } + }else if (cfg.matches("^[0-5]??[0-9]??\\-[0-5]??[0-9]??$")){//1-3 + String[] split = cfg.split("-"); + int s = Integer.parseInt(split[0]); + int e = Integer.parseInt(split[1]); + + hours = new int[e-s +1]; + for (int i = 0; i < hours.length; i++) { + hours[i] = s+i; + } + }else if (cfg.matches("^[0-5]??[0-9]??\\-[0-5]??[0-9]??/[0-5]??[0-9]??$")){//3-18/5 + String[] strArr = cfg.split("/"); + String[] str2Arr = strArr[0].split("-"); + int s = Integer.parseInt(str2Arr[0]); + int e = Integer.parseInt(str2Arr[1]); + int p = Integer.parseInt(strArr[1]); + + hours = new int[(e-s)/p]; + for (int i = 0; i < hours.length; i++) { + hours[i] = s + i*p; + } + } + } + + private void setWeeks(){ + String cfg = cfgArr[4]; + if ("*".equals(cfg)){//* + weeks = new int[7]; + for (int i = 0; i < weeks.length; i++) { + weeks[i] = i+1; + } + } else if (cfg.matches("^[0-5]??[0-9]??$")){//n + weeks = new int[1]; + weeks[0] = Integer.parseInt(cfg); + }else if (cfg.matches("^[*]/[0-9]+$")){// */5 + String[] strArr = cfg.split("/"); + int p = Integer.parseInt(strArr[1]); + weeks = new int[7/p]; + for (int i = 0; i < weeks.length; i++) { + weeks[i] = i*p; + } + }else if (cfg.matches("^([0-5]??[0-9]??,)+([0-5]??[0-9]??)?$")){//1,3 + String[] strArr = cfg.split(","); + weeks = new int[strArr.length]; + for (int i = 0; i < strArr.length; i++) { + weeks[i] = Integer.parseInt(strArr[i]); + } + }else if (cfg.matches("^[0-5]??[0-9]??\\-[0-5]??[0-9]??$")){//1-3 + String[] split = cfg.split("-"); + int s = Integer.parseInt(split[0]); + int e = Integer.parseInt(split[1]); + + weeks = new int[e-s +1]; + for (int i = 0; i < weeks.length; i++) { + weeks[i] = s+i; + } + }else if (cfg.matches("^[0-5]??[0-9]??\\-[0-5]??[0-9]??/[0-5]??[0-9]??$")){//3-18/5 + String[] strArr = cfg.split("/"); + String[] str2Arr = strArr[0].split("-"); + int s = Integer.parseInt(str2Arr[0]); + int e = Integer.parseInt(str2Arr[1]); + int p = Integer.parseInt(strArr[1]); + + weeks = new int[(e-s)/p]; + for (int i = 0; i < weeks.length; i++) { + weeks[i] = s + i*p; + } + } + } + + private void setMonthes(){ + String cfg = cfgArr[3]; + if ("*".equals(cfg)){//* + monthes = new int[12]; + for (int i = 0; i < monthes.length; i++) { + monthes[i] = i+1; + } + } else if (cfg.matches("^[0-5]??[0-9]??$")){//n + monthes = new int[1]; + monthes[0] = Integer.parseInt(cfg); + }else if (cfg.matches("^[*]/[0-9]+$")){// */5 + String[] strArr = cfg.split("/"); + int p = Integer.parseInt(strArr[1]); + monthes = new int[12/p]; + for (int i = 0; i < monthes.length; i++) { + monthes[i] = i*p; + } + }else if (cfg.matches("^([0-5]??[0-9]??,)+([0-5]??[0-9]??)?$")){//1,3 + String[] strArr = cfg.split(","); + monthes = new int[strArr.length]; + for (int i = 0; i < strArr.length; i++) { + monthes[i] = Integer.parseInt(strArr[i]); + } + }else if (cfg.matches("^[0-5]??[0-9]??\\-[0-5]??[0-9]??$")){//1-3 + String[] split = cfg.split("-"); + int s = Integer.parseInt(split[0]); + int e = Integer.parseInt(split[1]); + + monthes = new int[e-s +1]; + for (int i = 0; i < monthes.length; i++) { + monthes[i] = s+i; + } + }else if (cfg.matches("^[0-5]??[0-9]??\\-[0-5]??[0-9]??/[0-5]??[0-9]??$")){//3-18/5 + String[] strArr = cfg.split("/"); + String[] str2Arr = strArr[0].split("-"); + int s = Integer.parseInt(str2Arr[0]); + int e = Integer.parseInt(str2Arr[1]); + int p = Integer.parseInt(strArr[1]); + + monthes = new int[(e-s)/p]; + for (int i = 0; i < monthes.length; i++) { + monthes[i] = s + i*p; + } + } + } + + private void setDays(){ + String cfg = cfgArr[2]; + //当前月份总天数, + LocalDate firstDay = LocalDate.of(year, month, 1); + int lengthOfMonth = firstDay.lengthOfMonth(); + + if ("*".equals(cfg)){//* + days = new int[lengthOfMonth]; + for (int i = 0; i < days.length; i++) { + days[i] = i+1; + } + } else if (cfg.matches("^[0-5]??[0-9]??$")){//n + days = new int[1]; + days[0] = Integer.parseInt(cfg); + }else if (cfg.matches("^[*]/[0-9]+$")){// */5 + String[] strArr = cfg.split("/"); + int p = Integer.parseInt(strArr[1]); + days = new int[lengthOfMonth/p]; + for (int i = 0; i < days.length; i++) { + days[i] = i*p; + } + }else if (cfg.matches("^([0-5]??[0-9]??,)+([0-5]??[0-9]??)?$")){//1,3 + String[] strArr = cfg.split(","); + days = new int[strArr.length]; + for (int i = 0; i < strArr.length; i++) { + days[i] = Integer.parseInt(strArr[i]); + } + }else if (cfg.matches("^[0-5]??[0-9]??\\-[0-5]??[0-9]??$")){//1-3 + String[] split = cfg.split("-"); + int s = Integer.parseInt(split[0]); + int e = Integer.parseInt(split[1]); + + days = new int[e-s +1]; + for (int i = 0; i < days.length; i++) { + days[i] = s+i; + } + }else if (cfg.matches("^[0-5]??[0-9]??\\-[0-5]??[0-9]??/[0-5]??[0-9]??$")){//3-18/5 + String[] strArr = cfg.split("/"); + String[] str2Arr = strArr[0].split("-"); + int s = Integer.parseInt(str2Arr[0]); + int e = Integer.parseInt(str2Arr[1]); + int p = Integer.parseInt(strArr[1]); + + days = new int[(e-s)/p]; + for (int i = 0; i < days.length; i++) { + days[i] = s + i*p; + } + } + + int firstWeek = firstDay.getDayOfWeek().getValue(); + List allDay = new ArrayList<>(); + for (int i = 0; i < days.length; i++) { + //int week = 7 - Math.abs(i - firstWeek) % 7;//当前星期X + int week; + int d = days[i]; + if (d + firstWeek <= 8){ + week = firstWeek + d - 1; + }else { + week = (d - (8-firstWeek))%7; + if (week == 0) week = 7; + } + + //System.out.printf("M:%s,d:%s,w:%s%n", month, d, week); + + if (Arrays.binarySearch(weeks, week) > -1){ + allDay.add(d);//加入日期 + } + } + + days = new int[allDay.size()]; + for (int i = 0; i < allDay.size(); i++) { + days[i] = allDay.get(i); + } + } + +} diff --git a/src/test/java/com/lxyer/timer/TaskImpl.java b/src/test/java/com/lxyer/timer/TaskImpl.java new file mode 100644 index 0000000..1f1bac0 --- /dev/null +++ b/src/test/java/com/lxyer/timer/TaskImpl.java @@ -0,0 +1,34 @@ +package com.lxyer.timer; + +import com.lxyer.timer.scheduled.Scheduled; + +import java.text.SimpleDateFormat; + +/** + * @author: liangxianyou + * @createtime: 2018/8/5 20:39. + */ +public class TaskImpl extends AbstractTask { + public TaskImpl(String name, Scheduled scheduled) { + super(name, scheduled); + } + + @Override + public void run() { + System.out.println("----"); + System.out.println(new SimpleDateFormat("0: yyyy-MM-dd HH:mm:ss").format(theTime())); + System.out.println(new SimpleDateFormat("1: yyyy-MM-dd HH:mm:ss").format(nextTime())); + System.out.println(new SimpleDateFormat("2: yyyy-MM-dd HH:mm:ss").format(nextTime())); + System.out.println(new SimpleDateFormat("3: yyyy-MM-dd HH:mm:ss").format(nextTime())); + System.out.println(new SimpleDateFormat("4: yyyy-MM-dd HH:mm:ss").format(nextTime())); + System.out.println(new SimpleDateFormat("5: yyyy-MM-dd HH:mm:ss").format(nextTime())); + System.out.println(new SimpleDateFormat("6: yyyy-MM-dd HH:mm:ss").format(nextTime())); + System.out.println(new SimpleDateFormat("7: yyyy-MM-dd HH:mm:ss").format(nextTime())); + System.out.println(new SimpleDateFormat("8: yyyy-MM-dd HH:mm:ss").format(nextTime())); + System.out.println(new SimpleDateFormat("9: yyyy-MM-dd HH:mm:ss").format(nextTime())); + System.out.println(new SimpleDateFormat("10: yyyy-MM-dd HH:mm:ss").format(nextTime())); + System.out.println(new SimpleDateFormat("11: yyyy-MM-dd HH:mm:ss").format(nextTime())); + System.out.println(new SimpleDateFormat("12: yyyy-MM-dd HH:mm:ss").format(nextTime())); + System.out.println(new SimpleDateFormat("13: yyyy-MM-dd HH:mm:ss").format(nextTime())); + } +} diff --git a/src/test/java/com/lxyer/timer/TimerTest.java b/src/test/java/com/lxyer/timer/TimerTest.java index 33e785f..8f14f4a 100644 --- a/src/test/java/com/lxyer/timer/TimerTest.java +++ b/src/test/java/com/lxyer/timer/TimerTest.java @@ -1,5 +1,6 @@ package com.lxyer.timer; +import com.lxyer.timer.scheduled.ScheduledExpres; import org.junit.Test; import java.text.SimpleDateFormat; @@ -21,7 +22,7 @@ public class TimerTest { public void t2(){ TimerExecutor timerExecutor = new TimerExecutor(1); - timerExecutor.add(new Task("a1", "1-40 * * * *") { + timerExecutor.add(new TaskImpl("a1", new ScheduledExpres(LocalDateTime.now(), "1-40 * * * *")) { @Override public void run() { ThreadLocal local = new ThreadLocal<>(); @@ -132,55 +133,20 @@ public class TimerTest { } - /** - * LocalDateTime的一些api测试 - */ - @Test - public void t5(){ - //Minute * 1,3 1-3 */5 3-15/5 - String cfg = "* * * * *"; - LocalDateTime time = LocalDateTime.of(2018, 7, 18, 11, 11); - - int minute = time.getMinute(); - int hour = time.getHour(); - int day = time.getDayOfMonth(); - int month = time.getMonthValue(); - int week = time.getDayOfWeek().getValue(); - - //checkCfg - //if (month) - System.out.println(week); - } - /** * 测试配置的表达式 */ @Test public void t6(){ //分 时 日 月 周 + TimerExecutor executor = new TimerExecutor(10); + /* 08 18 * 7,8 4 + "task1", "1 22-23 * * 7" */ - new Task("task1", "1 22-23 * * 7") { - @Override - public void run() { - System.out.println("----"); - System.out.println(new SimpleDateFormat("0: yyyy-MM-dd HH:mm:ss").format(nextTime)); - System.out.println(new SimpleDateFormat("1: yyyy-MM-dd HH:mm:ss").format(nextTime())); - System.out.println(new SimpleDateFormat("2: yyyy-MM-dd HH:mm:ss").format(nextTime())); - System.out.println(new SimpleDateFormat("3: yyyy-MM-dd HH:mm:ss").format(nextTime())); - System.out.println(new SimpleDateFormat("4: yyyy-MM-dd HH:mm:ss").format(nextTime())); - System.out.println(new SimpleDateFormat("5: yyyy-MM-dd HH:mm:ss").format(nextTime())); - System.out.println(new SimpleDateFormat("6: yyyy-MM-dd HH:mm:ss").format(nextTime())); - System.out.println(new SimpleDateFormat("7: yyyy-MM-dd HH:mm:ss").format(nextTime())); - System.out.println(new SimpleDateFormat("8: yyyy-MM-dd HH:mm:ss").format(nextTime())); - System.out.println(new SimpleDateFormat("9: yyyy-MM-dd HH:mm:ss").format(nextTime())); - System.out.println(new SimpleDateFormat("10: yyyy-MM-dd HH:mm:ss").format(nextTime())); - System.out.println(new SimpleDateFormat("11: yyyy-MM-dd HH:mm:ss").format(nextTime())); - System.out.println(new SimpleDateFormat("12: yyyy-MM-dd HH:mm:ss").format(nextTime())); - System.out.println(new SimpleDateFormat("13: yyyy-MM-dd HH:mm:ss").format(nextTime())); - } - }.run(); + Task task = new TaskImpl("task1", new ScheduledExpres("1 22-23 * * 7")); + task.run(); } }