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();