diff --git a/hutool-cron/src/main/java/cn/hutool/v7/cron/CronConfig.java b/hutool-cron/src/main/java/cn/hutool/v7/cron/CronConfig.java
index 1b31192d47..c280145217 100644
--- a/hutool-cron/src/main/java/cn/hutool/v7/cron/CronConfig.java
+++ b/hutool-cron/src/main/java/cn/hutool/v7/cron/CronConfig.java
@@ -29,16 +29,20 @@ public class CronConfig {
/**
* 时区
*/
- protected TimeZone timezone = TimeZone.getDefault();
+ private TimeZone timezone = TimeZone.getDefault();
/**
* 是否支持秒匹配
*/
- protected boolean matchSecond;
+ private boolean matchSecond;
+ /**
+ * 是否为守护线程
+ */
+ private boolean daemon;
/**
* 构造
*/
- public CronConfig(){
+ public CronConfig() {
}
/**
@@ -80,4 +84,24 @@ public class CronConfig {
this.matchSecond = isMatchSecond;
return this;
}
+
+ /**
+ * 是否为守护线程
+ *
+ * @return {@code true}守护线程,{@code false}非守护线程
+ */
+ public boolean isDaemon() {
+ return this.daemon;
+ }
+
+ /**
+ * 设置是否为守护线程
+ *
+ * @param daemon {@code true}守护线程,{@code false}非守护线程
+ * @return this
+ */
+ public CronConfig setDaemon(final boolean daemon) {
+ this.daemon = daemon;
+ return this;
+ }
}
diff --git a/hutool-cron/src/main/java/cn/hutool/v7/cron/CronTimer.java b/hutool-cron/src/main/java/cn/hutool/v7/cron/CronTimer.java
index e27c61eb11..35c90c5c16 100644
--- a/hutool-cron/src/main/java/cn/hutool/v7/cron/CronTimer.java
+++ b/hutool-cron/src/main/java/cn/hutool/v7/cron/CronTimer.java
@@ -54,7 +54,7 @@ public class CronTimer extends Thread implements Serializable {
@Override
public void run() {
- final long timerUnit = this.scheduler.config.matchSecond ? TIMER_UNIT_SECOND : TIMER_UNIT_MINUTE;
+ final long timerUnit = this.scheduler.config.isMatchSecond() ? TIMER_UNIT_SECOND : TIMER_UNIT_MINUTE;
final long doubleTimeUnit = 2 * timerUnit;
long thisTime = System.currentTimeMillis();
diff --git a/hutool-cron/src/main/java/cn/hutool/v7/cron/Scheduler.java b/hutool-cron/src/main/java/cn/hutool/v7/cron/Scheduler.java
index f3efefcd01..bea067ca33 100644
--- a/hutool-cron/src/main/java/cn/hutool/v7/cron/Scheduler.java
+++ b/hutool-cron/src/main/java/cn/hutool/v7/cron/Scheduler.java
@@ -16,12 +16,12 @@
package cn.hutool.v7.cron;
+import cn.hutool.v7.core.data.id.IdUtil;
import cn.hutool.v7.core.map.MapUtil;
+import cn.hutool.v7.core.text.CharUtil;
+import cn.hutool.v7.core.text.StrUtil;
import cn.hutool.v7.core.thread.ExecutorBuilder;
import cn.hutool.v7.core.thread.ThreadFactoryBuilder;
-import cn.hutool.v7.core.text.CharUtil;
-import cn.hutool.v7.core.data.id.IdUtil;
-import cn.hutool.v7.core.text.StrUtil;
import cn.hutool.v7.cron.listener.TaskListener;
import cn.hutool.v7.cron.listener.TaskListenerManager;
import cn.hutool.v7.cron.pattern.CronPattern;
@@ -35,30 +35,29 @@ import java.io.Serial;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
-import java.util.TimeZone;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 任务调度器
- *
+ *
* 调度器启动流程:
*
*
* 启动Timer =》 启动TaskLauncher =》 启动TaskExecutor
*
- *
+ *
* 调度器关闭流程:
*
*
* 关闭Timer =》 关闭所有运行中的TaskLauncher =》 关闭所有运行中的TaskExecutor
*
- *
+ *
* 其中:
*
*
- * TaskLauncher:定时器每分钟调用一次(如果{@link Scheduler#isMatchSecond()}为{@code true}每秒调用一次),
+ * TaskLauncher:定时器每分钟调用一次(如果{@link CronConfig#isMatchSecond()}为{@code true}每秒调用一次),
* 负责检查TaskTable是否有匹配到此时间运行的Task
*
*
@@ -73,65 +72,58 @@ public class Scheduler implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
- private final Lock lock = new ReentrantLock();
+ /**
+ * 定时任务锁,用于同步添加和删除操作
+ */
+ private final Lock lock;
+ /**
+ * 定时任务配置
+ */
+ protected final CronConfig config;
- /** 定时任务配置 */
- protected CronConfig config = new CronConfig();
- /** 是否已经启动 */
- private boolean started = false;
- /** 是否为守护线程 */
- protected boolean daemon;
-
- /** 定时器 */
+ /**
+ * 是否已经启动
+ */
+ private boolean started;
+ /**
+ * 定时器
+ */
private CronTimer timer;
- /** 定时任务表 */
- protected TaskTable taskTable = new TaskTable();
- /** 线程池,用于执行TaskLauncher和TaskExecutor */
+ /**
+ * 定时任务表
+ */
+ protected TaskTable taskTable;
+ /**
+ * 线程池,用于执行TaskLauncher和TaskExecutor
+ */
protected ExecutorService threadExecutor;
- /** 任务管理器 */
+ /**
+ * 任务管理器
+ */
protected TaskManager taskManager;
- /** 监听管理器列表 */
- protected TaskListenerManager listenerManager = new TaskListenerManager();
-
- // --------------------------------------------------------- Getters and Setters start
/**
- * 设置时区
- *
- * @param timeZone 时区
- * @return this
+ * 监听管理器列表
*/
- public Scheduler setTimeZone(final TimeZone timeZone) {
- this.config.setTimeZone(timeZone);
- return this;
+ protected TaskListenerManager listenerManager;
+
+ /**
+ * 构造
+ * 使用默认配置
+ */
+ public Scheduler() {
+ this(new CronConfig());
}
/**
- * 获得时区,默认为 {@link TimeZone#getDefault()}
+ * 构造
*
- * @return 时区
+ * @param config 定时任务配置
*/
- public TimeZone getTimeZone() {
- return this.config.getTimeZone();
- }
-
- /**
- * 设置是否为守护线程
- * 如果为true,则在调用{@link #stop()}方法后执行的定时任务立即结束,否则等待执行完毕才结束。默认非守护线程
- * 如果用户调用{@link #setThreadExecutor(ExecutorService)}自定义线程池则此参数无效
- *
- * @param on {@code true}为守护线程,否则非守护线程
- * @return this
- * @throws CronException 定时任务已经启动抛出此异常
- */
- public Scheduler setDaemon(final boolean on) throws CronException {
- lock.lock();
- try {
- checkStarted();
- this.daemon = on;
- } finally {
- lock.unlock();
- }
- return this;
+ public Scheduler(final CronConfig config) {
+ this.config = config;
+ lock = new ReentrantLock();
+ this.taskTable = new TaskTable();
+ this.listenerManager = new TaskListenerManager();
}
/**
@@ -154,24 +146,6 @@ public class Scheduler implements Serializable {
return this;
}
- /**
- * 是否为守护线程
- *
- * @return 是否为守护线程
- */
- public boolean isDaemon() {
- return this.daemon;
- }
-
- /**
- * 是否支持秒匹配
- *
- * @return {@code true}使用,{@code false}不使用
- */
- public boolean isMatchSecond() {
- return this.config.isMatchSecond();
- }
-
/**
* 设置是否支持秒匹配,默认不使用
*
@@ -179,7 +153,13 @@ public class Scheduler implements Serializable {
* @return this
*/
public Scheduler setMatchSecond(final boolean isMatchSecond) {
- this.config.setMatchSecond(isMatchSecond);
+ lock.lock();
+ try {
+ checkStarted();
+ this.config.setMatchSecond(isMatchSecond);
+ } finally {
+ lock.unlock();
+ }
return this;
}
@@ -190,7 +170,12 @@ public class Scheduler implements Serializable {
* @return this
*/
public Scheduler addListener(final TaskListener listener) {
- this.listenerManager.addListener(listener);
+ lock.lock();
+ try {
+ this.listenerManager.addListener(listener);
+ } finally {
+ lock.unlock();
+ }
return this;
}
@@ -201,12 +186,17 @@ public class Scheduler implements Serializable {
* @return this
*/
public Scheduler removeListener(final TaskListener listener) {
- this.listenerManager.removeListener(listener);
+ lock.lock();
+ try {
+ this.listenerManager.removeListener(listener);
+ } finally {
+ lock.unlock();
+ }
return this;
}
- // --------------------------------------------------------- Getters and Setters end
// region ----- schedule
+
/**
* 批量加入配置文件中的定时任务
* 配置文件格式为: xxx.xxx.xxx.Class.method = * * * * *
@@ -242,7 +232,7 @@ public class Scheduler implements Serializable {
* 新增Task,使用随机UUID
*
* @param pattern {@link CronPattern}对应的String表达式
- * @param task {@link Task}
+ * @param task {@link Task}
* @return ID
*/
public String schedule(final String pattern, final Task task) {
@@ -254,9 +244,9 @@ public class Scheduler implements Serializable {
/**
* 新增Task,如果任务ID已经存在,抛出异常
*
- * @param id ID,为每一个Task定义一个ID
+ * @param id ID,为每一个Task定义一个ID
* @param pattern {@link CronPattern}对应的String表达式
- * @param task {@link Runnable}
+ * @param task {@link Runnable}
* @return this
*/
public Scheduler schedule(final String id, final String pattern, final Runnable task) {
@@ -266,9 +256,9 @@ public class Scheduler implements Serializable {
/**
* 新增Task,如果任务ID已经存在,抛出异常
*
- * @param id ID,为每一个Task定义一个ID
+ * @param id ID,为每一个Task定义一个ID
* @param pattern {@link CronPattern}对应的String表达式
- * @param task {@link Task}
+ * @param task {@link Task}
* @return this
*/
public Scheduler schedule(final String id, final String pattern, final Task task) {
@@ -278,9 +268,9 @@ public class Scheduler implements Serializable {
/**
* 新增Task,如果任务ID已经存在,抛出异常
*
- * @param id ID,为每一个Task定义一个ID
+ * @param id ID,为每一个Task定义一个ID
* @param pattern {@link CronPattern}
- * @param task {@link Task}
+ * @param task {@link Task}
* @return this
*/
public Scheduler schedule(final String id, final CronPattern pattern, final Task task) {
@@ -314,7 +304,7 @@ public class Scheduler implements Serializable {
/**
* 更新Task执行的时间规则
*
- * @param id Task的ID
+ * @param id Task的ID
* @param pattern {@link CronPattern}
* @return this
* @since 4.0.10
@@ -378,6 +368,7 @@ public class Scheduler implements Serializable {
/**
* 清空任务表
+ *
* @return this
* @since 4.1.17
*/
@@ -400,7 +391,7 @@ public class Scheduler implements Serializable {
* @return this
*/
public Scheduler start(final boolean isDaemon) {
- this.daemon = isDaemon;
+ this.config.setDaemon(isDaemon);
return start();
}
@@ -410,21 +401,23 @@ public class Scheduler implements Serializable {
* @return this
*/
public Scheduler start() {
+ final boolean daemon = this.config.isDaemon();
+
lock.lock();
try {
checkStarted();
- if(null == this.threadExecutor){
+ if (null == this.threadExecutor) {
// 无界线程池,确保每一个需要执行的线程都可以及时运行,同时复用已有线程避免线程重复创建
this.threadExecutor = ExecutorBuilder.of().useSynchronousQueue().setThreadFactory(//
- ThreadFactoryBuilder.of().setNamePrefix("hutool-cron-").setDaemon(this.daemon).build()//
+ ThreadFactoryBuilder.of().setNamePrefix("hutool-cron-").setDaemon(daemon).build()//
).build();
}
this.taskManager = new TaskManager(this);
// Start CronTimer
timer = new CronTimer(this);
- timer.setDaemon(this.daemon);
+ timer.setDaemon(daemon);
timer.start();
this.started = true;
} finally {
@@ -468,7 +461,7 @@ public class Scheduler implements Serializable {
this.threadExecutor = null;
//可选是否清空任务表
- if(clearTasks) {
+ if (clearTasks) {
clear();
}
@@ -485,7 +478,7 @@ public class Scheduler implements Serializable {
*
* @throws CronException 已经启动则抛出此异常
*/
- private void checkStarted() throws CronException{
+ private void checkStarted() throws CronException {
if (this.started) {
throw new CronException("Scheduler already started!");
}
diff --git a/hutool-cron/src/main/java/cn/hutool/v7/cron/TaskTable.java b/hutool-cron/src/main/java/cn/hutool/v7/cron/TaskTable.java
index 5cd2645deb..b6dce6d408 100644
--- a/hutool-cron/src/main/java/cn/hutool/v7/cron/TaskTable.java
+++ b/hutool-cron/src/main/java/cn/hutool/v7/cron/TaskTable.java
@@ -45,8 +45,14 @@ public class TaskTable implements Serializable {
*/
public static final int DEFAULT_CAPACITY = 10;
+ /**
+ * 读写锁,保证线程安全
+ */
private final ReadWriteLock lock;
+ /**
+ * 任务表,ID、表达式、任务一一对应。使用TripleTable存储,便于快速查找和更新
+ */
private final TripleTable table;
/**
@@ -273,7 +279,7 @@ public class TaskTable implements Serializable {
* 如果时间匹配则执行相应的Task,带读锁
*
* @param scheduler {@link Scheduler}
- * @param millis 时间毫秒
+ * @param millis 时间毫秒
*/
public void executeTaskIfMatch(final Scheduler scheduler, final long millis) {
final Lock readLock = lock.readLock();
@@ -291,7 +297,7 @@ public class TaskTable implements Serializable {
final StringBuilder builder = StrUtil.builder();
for (int i = 0; i < size; i++) {
builder.append(StrUtil.format("[{}] [{}] [{}]\n",
- this.table.getLeft(i), this.table.getMiddle(i), this.table.getRight(i)));
+ this.table.getLeft(i), this.table.getMiddle(i), this.table.getRight(i)));
}
return builder.toString();
}
@@ -300,13 +306,13 @@ public class TaskTable implements Serializable {
* 如果时间匹配则执行相应的Task,无锁
*
* @param scheduler {@link Scheduler}
- * @param millis 时间毫秒
+ * @param millis 时间毫秒
* @since 3.1.1
*/
private void executeTaskIfMatchInternal(final Scheduler scheduler, final long millis) {
final int size = size();
for (int i = 0; i < size; i++) {
- if (this.table.getMiddle(i).match(scheduler.config.timezone, millis, scheduler.config.matchSecond)) {
+ if (this.table.getMiddle(i).match(scheduler.config.getTimeZone(), millis, scheduler.config.isMatchSecond())) {
scheduler.taskManager.spawnExecutor(
new CronTask(this.table.getLeft(i), this.table.getMiddle(i), this.table.getRight(i)));
}
diff --git a/hutool-cron/src/main/java/cn/hutool/v7/cron/pattern/CronPattern.java b/hutool-cron/src/main/java/cn/hutool/v7/cron/pattern/CronPattern.java
index 846365401b..4a2b07721a 100644
--- a/hutool-cron/src/main/java/cn/hutool/v7/cron/pattern/CronPattern.java
+++ b/hutool-cron/src/main/java/cn/hutool/v7/cron/pattern/CronPattern.java
@@ -18,8 +18,6 @@ package cn.hutool.v7.cron.pattern;
import cn.hutool.v7.core.comparator.CompareUtil;
import cn.hutool.v7.core.date.CalendarUtil;
-import cn.hutool.v7.core.date.DateUtil;
-import cn.hutool.v7.core.lang.Console;
import cn.hutool.v7.cron.pattern.matcher.PatternMatcher;
import cn.hutool.v7.cron.pattern.parser.PatternParser;
diff --git a/hutool-cron/src/test/java/cn/hutool/v7/cron/demo/CronTest.java b/hutool-cron/src/test/java/cn/hutool/v7/cron/demo/CronTest.java
index a3542a834b..81a44dd5f5 100644
--- a/hutool-cron/src/test/java/cn/hutool/v7/cron/demo/CronTest.java
+++ b/hutool-cron/src/test/java/cn/hutool/v7/cron/demo/CronTest.java
@@ -47,8 +47,7 @@ public class CronTest {
public void cronTest() {
// 支持秒级别定时任务
CronUtil.setMatchSecond(true);
- CronUtil.getScheduler().setDaemon(false);
- CronUtil.start();
+ CronUtil.start(false);
ThreadUtil.waitForDie();
CronUtil.stop();