|
|
|
@ -0,0 +1,217 @@
|
|
|
|
|
package xyz.wbsite.jmacro;
|
|
|
|
|
|
|
|
|
|
import java.time.LocalTime;
|
|
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
|
import java.util.concurrent.Executors;
|
|
|
|
|
import java.util.concurrent.ScheduledExecutorService;
|
|
|
|
|
import java.util.concurrent.ScheduledFuture;
|
|
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* JScheduler: 轻量调度器,基于 ScheduledExecutorService
|
|
|
|
|
* <p>
|
|
|
|
|
* 1. 支持循环执行
|
|
|
|
|
* 2. 支持定时执行
|
|
|
|
|
*
|
|
|
|
|
* @author wangbing
|
|
|
|
|
* @version 0.0.1
|
|
|
|
|
* @since 1.8
|
|
|
|
|
*/
|
|
|
|
|
public class JScheduler {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 单线程调度器
|
|
|
|
|
*/
|
|
|
|
|
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 任务映射表,用于存储任务ID和对应的 ScheduledFuture<?> 对象
|
|
|
|
|
*/
|
|
|
|
|
private final Map<String, ScheduledFuture<?>> tasks = new ConcurrentHashMap<>();
|
|
|
|
|
|
|
|
|
|
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 添加固定延迟循环任务(无限)
|
|
|
|
|
*
|
|
|
|
|
* @param taskId 任务ID
|
|
|
|
|
* @param task 任务
|
|
|
|
|
* @param initialDelay 初始延迟
|
|
|
|
|
* @param delay 延迟
|
|
|
|
|
* @param unit 时间单位
|
|
|
|
|
*/
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 添加固定次数循环任务
|
|
|
|
|
*
|
|
|
|
|
* @param taskId 任务ID
|
|
|
|
|
* @param task 任务
|
|
|
|
|
* @param initialDelay 初始延迟
|
|
|
|
|
* @param period 周期
|
|
|
|
|
* @param unit 时间单位
|
|
|
|
|
* @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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 添加多时间点任务(例如 "08:30:00,12:00:00,18:00:00")
|
|
|
|
|
*
|
|
|
|
|
* @param taskId 任务ID
|
|
|
|
|
* @param task 任务
|
|
|
|
|
* @param timePoints 时间点字符串(逗号分隔)
|
|
|
|
|
*/
|
|
|
|
|
public void scheduleAtTimes(String taskId, Runnable task, String timePoints) {
|
|
|
|
|
cancelTask(taskId);
|
|
|
|
|
|
|
|
|
|
List<LocalTime> times = parseTimePoints(timePoints);
|
|
|
|
|
if (times.isEmpty()) return;
|
|
|
|
|
|
|
|
|
|
Runnable timeChecker = () -> {
|
|
|
|
|
LocalTime now = LocalTime.now();
|
|
|
|
|
for (LocalTime scheduledTime : times) {
|
|
|
|
|
if (isSameTime(now, scheduledTime)) {
|
|
|
|
|
task.run();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 每秒检查一次
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 取消指定任务
|
|
|
|
|
*
|
|
|
|
|
* @param taskId 任务ID
|
|
|
|
|
*/
|
|
|
|
|
public void cancelTask(String taskId) {
|
|
|
|
|
ScheduledFuture<?> future = tasks.remove(taskId);
|
|
|
|
|
if (future != null) {
|
|
|
|
|
future.cancel(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 关闭调度器
|
|
|
|
|
*/
|
|
|
|
|
public void shutdown() {
|
|
|
|
|
executor.shutdown();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 解析多时间点字符串
|
|
|
|
|
*
|
|
|
|
|
* @param timePoints 时间点字符串(逗号分隔)
|
|
|
|
|
* @return 解析后的时间列表
|
|
|
|
|
*/
|
|
|
|
|
private List<LocalTime> parseTimePoints(String timePoints) {
|
|
|
|
|
List<LocalTime> result = new ArrayList<>();
|
|
|
|
|
if (timePoints == null || timePoints.trim().isEmpty()) return result;
|
|
|
|
|
|
|
|
|
|
String[] parts = timePoints.split(",");
|
|
|
|
|
for (String part : parts) {
|
|
|
|
|
try {
|
|
|
|
|
result.add(LocalTime.parse(part.trim(), TIME_FORMATTER));
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
System.err.println("无效的时间格式: " + part.trim());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 有限次数任务包装器
|
|
|
|
|
*/
|
|
|
|
|
private class LimitedTask implements Runnable {
|
|
|
|
|
private final Runnable task;
|
|
|
|
|
private int remainingTimes;
|
|
|
|
|
|
|
|
|
|
LimitedTask(Runnable task, int times) {
|
|
|
|
|
this.task = task;
|
|
|
|
|
this.remainingTimes = times;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
if (remainingTimes > 0) {
|
|
|
|
|
task.run();
|
|
|
|
|
remainingTimes--;
|
|
|
|
|
if (remainingTimes == 0) {
|
|
|
|
|
// 任务执行完毕,取消自己
|
|
|
|
|
for (Map.Entry<String, ScheduledFuture<?>> entry : tasks.entrySet()) {
|
|
|
|
|
if (entry.getValue().isDone()) {
|
|
|
|
|
tasks.remove(entry.getKey());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void main(String[] args) {
|
|
|
|
|
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",
|
|
|
|
|
() -> System.out.println("定时任务执行: " + LocalTime.now()),
|
|
|
|
|
"08:30:00,12:00:00,18:00:00");
|
|
|
|
|
|
|
|
|
|
// 运行一段时间后关闭
|
|
|
|
|
try {
|
|
|
|
|
Thread.sleep(10_000);
|
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scheduler.cancelAllTask();
|
|
|
|
|
|
|
|
|
|
scheduler.shutdown();
|
|
|
|
|
}
|
|
|
|
|
}
|