|
|
|
@ -3,9 +3,13 @@ package xyz.wbsite.jmacro;
|
|
|
|
|
import java.time.LocalTime;
|
|
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.Queue;
|
|
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
|
|
|
import java.util.concurrent.ConcurrentSkipListSet;
|
|
|
|
|
import java.util.concurrent.Executors;
|
|
|
|
|
import java.util.concurrent.ScheduledExecutorService;
|
|
|
|
|
import java.util.concurrent.ScheduledFuture;
|
|
|
|
@ -31,88 +35,108 @@ public class JScheduler {
|
|
|
|
|
/**
|
|
|
|
|
* 任务映射表,用于存储任务ID和对应的 ScheduledFuture<?> 对象
|
|
|
|
|
*/
|
|
|
|
|
private final Map<String, ScheduledFuture<?>> tasks = new ConcurrentHashMap<>();
|
|
|
|
|
private final Queue<ScheduledFuture<?>> tasksHolder = new ConcurrentLinkedQueue<>();
|
|
|
|
|
|
|
|
|
|
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 添加固定延迟循环任务(无限)
|
|
|
|
|
* 添加固定延迟循环任务
|
|
|
|
|
*
|
|
|
|
|
* @param taskId 任务ID
|
|
|
|
|
* @param task 任务
|
|
|
|
|
* @param initialDelay 初始延迟
|
|
|
|
|
* @param delay 延迟
|
|
|
|
|
* @param unit 时间单位
|
|
|
|
|
* @param task 任务
|
|
|
|
|
* @param intervalSeconds 周期(单位秒)
|
|
|
|
|
*/
|
|
|
|
|
public void scheduleFixedDelay(String taskId, Runnable task, long initialDelay, long delay, TimeUnit unit) {
|
|
|
|
|
cancelTask(taskId);
|
|
|
|
|
ScheduledFuture<?> future = executor.scheduleWithFixedDelay(task, initialDelay, delay, unit);
|
|
|
|
|
tasks.put(taskId, future);
|
|
|
|
|
public void schedule(Runnable task, long intervalSeconds) {
|
|
|
|
|
ScheduledFuture<?> future = executor.scheduleWithFixedDelay(task, 0, intervalSeconds, TimeUnit.SECONDS);
|
|
|
|
|
tasksHolder.add(future);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 添加固定次数循环任务
|
|
|
|
|
*
|
|
|
|
|
* @param taskId 任务ID
|
|
|
|
|
* @param task 任务
|
|
|
|
|
* @param initialDelay 初始延迟
|
|
|
|
|
* @param period 周期
|
|
|
|
|
* @param unit 时间单位
|
|
|
|
|
* @param times 执行次数
|
|
|
|
|
* @param task 任务
|
|
|
|
|
* @param intervalSeconds 周期(单位秒)
|
|
|
|
|
* @param times 执行次数
|
|
|
|
|
*/
|
|
|
|
|
public void scheduleFixedTimes(String taskId, Runnable task, long initialDelay, long period, TimeUnit unit, int times) {
|
|
|
|
|
cancelTask(taskId);
|
|
|
|
|
ScheduledFuture<?> future = executor.scheduleWithFixedDelay(new LimitedTask(task, times), initialDelay, period, unit);
|
|
|
|
|
tasks.put(taskId, future);
|
|
|
|
|
public void schedule(Runnable task, long intervalSeconds, int times) {
|
|
|
|
|
ScheduledFuture<?> future = executor.scheduleWithFixedDelay(new LimitedTask(task, times), 0, intervalSeconds, TimeUnit.SECONDS);
|
|
|
|
|
tasksHolder.add(future);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final Map<Runnable, ConcurrentSkipListSet<Integer>> taskTimeMap = new ConcurrentHashMap<>();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 添加多时间点任务(例如 "08:30:00,12:00:00,18:00:00")
|
|
|
|
|
* 添加单时间点任务
|
|
|
|
|
*
|
|
|
|
|
* @param taskId 任务ID
|
|
|
|
|
* @param task 任务
|
|
|
|
|
* @param timePoints 时间点字符串(逗号分隔)
|
|
|
|
|
* @param task
|
|
|
|
|
* @param time
|
|
|
|
|
*/
|
|
|
|
|
public void scheduleAtTimes(String taskId, Runnable task, String timePoints) {
|
|
|
|
|
cancelTask(taskId);
|
|
|
|
|
public void schedule(Runnable task, String time) {
|
|
|
|
|
schedule(task, Arrays.asList(time.split(",")));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<LocalTime> times = parseTimePoints(timePoints);
|
|
|
|
|
if (times.isEmpty()) return;
|
|
|
|
|
/**
|
|
|
|
|
* 添加多时间点任务
|
|
|
|
|
* <p>
|
|
|
|
|
* 例如 "08:30:00"
|
|
|
|
|
*
|
|
|
|
|
* @param task 任务
|
|
|
|
|
* @param time 时间点
|
|
|
|
|
*/
|
|
|
|
|
public void schedule(Runnable task, List<String> time) {
|
|
|
|
|
if (time.isEmpty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Runnable timeChecker = () -> {
|
|
|
|
|
LocalTime now = LocalTime.now();
|
|
|
|
|
for (LocalTime scheduledTime : times) {
|
|
|
|
|
if (isSameTime(now, scheduledTime)) {
|
|
|
|
|
task.run();
|
|
|
|
|
// 转换为数字集合(秒级精度)
|
|
|
|
|
ConcurrentSkipListSet<Integer> timeSet = new ConcurrentSkipListSet<>();
|
|
|
|
|
Runnable trigger = () -> {
|
|
|
|
|
for (String string : time) {
|
|
|
|
|
int nowSeconds = LocalTime.now().toSecondOfDay();
|
|
|
|
|
int secondOfDay = LocalTime.parse(string, TIME_FORMATTER).toSecondOfDay();
|
|
|
|
|
if (secondOfDay < nowSeconds) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
timeSet.add(secondOfDay);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
trigger.run();
|
|
|
|
|
// 修正时间计算:计算到下一个凌晨的秒数
|
|
|
|
|
long nowSeconds = LocalTime.now().toSecondOfDay();
|
|
|
|
|
// 剩余秒数到次日凌晨
|
|
|
|
|
long delay = (24 * 3600 - nowSeconds) % (24 * 3600);
|
|
|
|
|
|
|
|
|
|
// 使用 scheduleAtFixedRate 保证每日执行
|
|
|
|
|
ScheduledFuture<?> triggerFuture = executor.scheduleAtFixedRate(
|
|
|
|
|
trigger,
|
|
|
|
|
delay,
|
|
|
|
|
24 * 3600, // 24小时周期
|
|
|
|
|
TimeUnit.SECONDS
|
|
|
|
|
);
|
|
|
|
|
tasksHolder.add(triggerFuture);
|
|
|
|
|
|
|
|
|
|
// 创建每秒检查的任务
|
|
|
|
|
Runnable checker = () -> {
|
|
|
|
|
int now = LocalTime.now().toSecondOfDay();
|
|
|
|
|
Integer nextTime = timeSet.floor(now);
|
|
|
|
|
if (nextTime != null) {
|
|
|
|
|
task.run();
|
|
|
|
|
timeSet.remove(nextTime);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 每秒检查一次
|
|
|
|
|
ScheduledFuture<?> future = executor.scheduleAtFixedRate(timeChecker, 0, 1, TimeUnit.SECONDS);
|
|
|
|
|
tasks.put(taskId, future);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 取消所有任务
|
|
|
|
|
*/
|
|
|
|
|
public void cancelAllTask() {
|
|
|
|
|
for (ScheduledFuture<?> future : tasks.values()) {
|
|
|
|
|
future.cancel(false);
|
|
|
|
|
}
|
|
|
|
|
tasks.clear();
|
|
|
|
|
ScheduledFuture<?> checkerFuture = executor.scheduleAtFixedRate(checker, 0, 1, TimeUnit.SECONDS);
|
|
|
|
|
tasksHolder.add(checkerFuture);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 取消指定任务
|
|
|
|
|
*
|
|
|
|
|
* @param taskId 任务ID
|
|
|
|
|
* 取消任务
|
|
|
|
|
*/
|
|
|
|
|
public void cancelTask(String taskId) {
|
|
|
|
|
ScheduledFuture<?> future = tasks.remove(taskId);
|
|
|
|
|
if (future != null) {
|
|
|
|
|
public void cancel() {
|
|
|
|
|
for (ScheduledFuture<?> future : tasksHolder) {
|
|
|
|
|
future.cancel(false);
|
|
|
|
|
}
|
|
|
|
|
tasksHolder.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -130,7 +154,9 @@ public class JScheduler {
|
|
|
|
|
*/
|
|
|
|
|
private List<LocalTime> parseTimePoints(String timePoints) {
|
|
|
|
|
List<LocalTime> result = new ArrayList<>();
|
|
|
|
|
if (timePoints == null || timePoints.trim().isEmpty()) return result;
|
|
|
|
|
if (timePoints == null || timePoints.trim().isEmpty()) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String[] parts = timePoints.split(",");
|
|
|
|
|
for (String part : parts) {
|
|
|
|
@ -143,19 +169,6 @@ public class JScheduler {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 判断两个时间是否相同(忽略毫秒)
|
|
|
|
|
*
|
|
|
|
|
* @param t1 时间1
|
|
|
|
|
* @param t2 时间2
|
|
|
|
|
* @return 是否相同
|
|
|
|
|
*/
|
|
|
|
|
private boolean isSameTime(LocalTime t1, LocalTime t2) {
|
|
|
|
|
return t1.getHour() == t2.getHour() &&
|
|
|
|
|
t1.getMinute() == t2.getMinute() &&
|
|
|
|
|
t1.getSecond() == t2.getSecond();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 有限次数任务包装器
|
|
|
|
|
*/
|
|
|
|
@ -175,9 +188,9 @@ public class JScheduler {
|
|
|
|
|
remainingTimes--;
|
|
|
|
|
if (remainingTimes == 0) {
|
|
|
|
|
// 任务执行完毕,取消自己
|
|
|
|
|
for (Map.Entry<String, ScheduledFuture<?>> entry : tasks.entrySet()) {
|
|
|
|
|
if (entry.getValue().isDone()) {
|
|
|
|
|
tasks.remove(entry.getKey());
|
|
|
|
|
for (ScheduledFuture<?> future : tasksHolder) {
|
|
|
|
|
if (future.isDone()) {
|
|
|
|
|
tasksHolder.remove(future);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -189,28 +202,27 @@ public class JScheduler {
|
|
|
|
|
JScheduler scheduler = new JScheduler();
|
|
|
|
|
|
|
|
|
|
// 1. 无限循环任务(每2秒执行一次)
|
|
|
|
|
scheduler.scheduleFixedDelay("task1",
|
|
|
|
|
() -> System.out.println("无限任务执行: " + LocalTime.now()),
|
|
|
|
|
0, 2, TimeUnit.SECONDS);
|
|
|
|
|
|
|
|
|
|
// 2. 有限次数任务(执行5次,每1秒一次)
|
|
|
|
|
scheduler.scheduleFixedTimes("task2",
|
|
|
|
|
() -> System.out.println("有限任务执行: " + LocalTime.now()),
|
|
|
|
|
0, 1, TimeUnit.SECONDS, 5);
|
|
|
|
|
|
|
|
|
|
// 3. 多时间点任务(每天 08:30:00, 12:00:00, 18:00:00 执行)
|
|
|
|
|
scheduler.scheduleAtTimes("task3",
|
|
|
|
|
// scheduler.schedule(() -> System.out.println("无限任务执行: " + LocalTime.now()),
|
|
|
|
|
// 2);
|
|
|
|
|
|
|
|
|
|
// // 2. 有限次数任务(执行5次,每1秒一次)
|
|
|
|
|
// scheduler.schedule(
|
|
|
|
|
// () -> System.out.println("有限任务执行: " + LocalTime.now()),
|
|
|
|
|
// 3, 5);
|
|
|
|
|
//
|
|
|
|
|
// // 3. 多时间点任务(每天 08:30:00, 12:00:00, 18:00:00 执行)
|
|
|
|
|
scheduler.schedule(
|
|
|
|
|
() -> System.out.println("定时任务执行: " + LocalTime.now()),
|
|
|
|
|
"08:30:00,12:00:00,18:00:00");
|
|
|
|
|
"12:58:00,18:00:00");
|
|
|
|
|
|
|
|
|
|
// 运行一段时间后关闭
|
|
|
|
|
try {
|
|
|
|
|
Thread.sleep(10_000);
|
|
|
|
|
Thread.sleep(60_000);
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scheduler.cancelAllTask();
|
|
|
|
|
scheduler.cancel();
|
|
|
|
|
|
|
|
|
|
scheduler.shutdown();
|
|
|
|
|
}
|
|
|
|
|