change cron

This commit is contained in:
Looly
2020-10-31 20:57:22 +08:00
parent 6674dcbde7
commit 905a4da3ee
12 changed files with 216 additions and 49 deletions

View File

@@ -3,7 +3,7 @@
------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------
# 5.4.7 (2020-10-29) # 5.4.7 (2020-10-31)
### 新特性 ### 新特性
* 【core 】 增加OptionalBeanpr#1182@Github * 【core 】 增加OptionalBeanpr#1182@Github
@@ -13,10 +13,12 @@
* 【http 】 增加SoapClient增加addSOAPHeader重载 * 【http 】 增加SoapClient增加addSOAPHeader重载
* 【http 】 ArrayUtil增加containsAll方法 * 【http 】 ArrayUtil增加containsAll方法
* 【http 】 增加CharsetDetector * 【http 】 增加CharsetDetector
* 【cron 】 增加CronTask监听支持获取idissue#I23315@Gitee
### Bug修复 ### Bug修复
* 【core 】 修复BeanUtil.beanToMap方法中editor返回null没有去掉的问题 * 【core 】 修复BeanUtil.beanToMap方法中editor返回null没有去掉的问题
* 【core 】 修复ImgUtil.toBufferedImage颜色模式的问题issue#1194@Github * 【core 】 修复ImgUtil.toBufferedImage颜色模式的问题issue#1194@Github
* 【cron 】 修复TimeZone设置无效的问题issue#I23315@Gitee
------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------

View File

@@ -0,0 +1,64 @@
package cn.hutool.cron;
import java.util.TimeZone;
/**
* 定时任务配置类
*
* @author looly
* @since 5.4.7
*/
public class CronConfig {
/**
* 时区
*/
protected TimeZone timezone = TimeZone.getDefault();
/**
* 是否支持秒匹配
*/
protected boolean matchSecond;
public CronConfig(){
}
/**
* 设置时区
*
* @param timezone 时区
* @return this
*/
public CronConfig setTimeZone(TimeZone timezone) {
this.timezone = timezone;
return this;
}
/**
* 获得时区,默认为 {@link TimeZone#getDefault()}
*
* @return 时区
*/
public TimeZone getTimeZone() {
return this.timezone;
}
/**
* 是否支持秒匹配
*
* @return <code>true</code>使用,<code>false</code>不使用
*/
public boolean isMatchSecond() {
return this.matchSecond;
}
/**
* 设置是否支持秒匹配,默认不使用
*
* @param isMatchSecond <code>true</code>支持,<code>false</code>不支持
* @return this
*/
public CronConfig setMatchSecond(boolean isMatchSecond) {
this.matchSecond = isMatchSecond;
return this;
}
}

View File

@@ -37,7 +37,7 @@ public class CronTimer extends Thread implements Serializable {
@Override @Override
public void run() { public void run() {
final long timerUnit = this.scheduler.matchSecond ? TIMER_UNIT_SECOND : TIMER_UNIT_MINUTE; final long timerUnit = this.scheduler.config.matchSecond ? TIMER_UNIT_SECOND : TIMER_UNIT_MINUTE;
long thisTime = System.currentTimeMillis(); long thisTime = System.currentTimeMillis();
long nextTime; long nextTime;

View File

@@ -57,19 +57,17 @@ public class Scheduler implements Serializable {
private final Lock lock = new ReentrantLock(); private final Lock lock = new ReentrantLock();
/** 时区 */ /** 定时任务配置 */
private TimeZone timezone; protected CronConfig config = new CronConfig();
/** 是否已经启动 */ /** 是否已经启动 */
private boolean started = false; private boolean started = false;
/** 是否支持秒匹配 */
protected boolean matchSecond = false;
/** 是否为守护线程 */ /** 是否为守护线程 */
protected boolean daemon; protected boolean daemon;
/** 定时器 */ /** 定时器 */
private CronTimer timer; private CronTimer timer;
/** 定时任务表 */ /** 定时任务表 */
protected TaskTable taskTable = new TaskTable(this); protected TaskTable taskTable = new TaskTable();
/** 启动器管理器 */ /** 启动器管理器 */
protected TaskLauncherManager taskLauncherManager; protected TaskLauncherManager taskLauncherManager;
/** 执行器管理器 */ /** 执行器管理器 */
@@ -83,11 +81,11 @@ public class Scheduler implements Serializable {
/** /**
* 设置时区 * 设置时区
* *
* @param timezone 时区 * @param timeZone 时区
* @return this * @return this
*/ */
public Scheduler setTimeZone(TimeZone timezone) { public Scheduler setTimeZone(TimeZone timeZone) {
this.timezone = timezone; this.config.setTimeZone(timeZone);
return this; return this;
} }
@@ -97,7 +95,7 @@ public class Scheduler implements Serializable {
* @return 时区 * @return 时区
*/ */
public TimeZone getTimeZone() { public TimeZone getTimeZone() {
return timezone != null ? timezone : TimeZone.getDefault(); return this.config.getTimeZone();
} }
/** /**
@@ -136,7 +134,7 @@ public class Scheduler implements Serializable {
* @return <code>true</code>使用,<code>false</code>不使用 * @return <code>true</code>使用,<code>false</code>不使用
*/ */
public boolean isMatchSecond() { public boolean isMatchSecond() {
return this.matchSecond; return this.config.isMatchSecond();
} }
/** /**
@@ -146,7 +144,7 @@ public class Scheduler implements Serializable {
* @return this * @return this
*/ */
public Scheduler setMatchSecond(boolean isMatchSecond) { public Scheduler setMatchSecond(boolean isMatchSecond) {
this.matchSecond = isMatchSecond; this.config.setMatchSecond(isMatchSecond);
return this; return this;
} }
@@ -347,7 +345,7 @@ public class Scheduler implements Serializable {
* @since 4.1.17 * @since 4.1.17
*/ */
public Scheduler clear() { public Scheduler clear() {
this.taskTable = new TaskTable(this); this.taskTable = new TaskTable();
return this; return this;
} }
// -------------------------------------------------------------------- shcedule end // -------------------------------------------------------------------- shcedule end

View File

@@ -1,5 +1,6 @@
package cn.hutool.cron; package cn.hutool.cron;
import cn.hutool.cron.task.CronTask;
import cn.hutool.cron.task.Task; import cn.hutool.cron.task.Task;
/** /**
@@ -12,15 +13,25 @@ import cn.hutool.cron.task.Task;
public class TaskExecutor implements Runnable { public class TaskExecutor implements Runnable {
private final Scheduler scheduler; private final Scheduler scheduler;
private final Task task; private final CronTask task;
/** /**
* 获得任务对象 * 获得原始任务对象
* *
* @return 任务对象 * @return 任务对象
*/ */
public Task getTask() { public Task getTask() {
return task; return this.task.getRaw();
}
/**
* 获得原始任务对象
*
* @return 任务对象
* @since 5.4.7
*/
public CronTask getCronTask() {
return this.task;
} }
/** /**
@@ -29,7 +40,7 @@ public class TaskExecutor implements Runnable {
* @param scheduler 调度器 * @param scheduler 调度器
* @param task 被执行的任务 * @param task 被执行的任务
*/ */
public TaskExecutor(Scheduler scheduler, Task task) { public TaskExecutor(Scheduler scheduler, CronTask task) {
this.scheduler = scheduler; this.scheduler = scheduler;
this.task = task; this.task = task;
} }

View File

@@ -1,12 +1,13 @@
package cn.hutool.cron; package cn.hutool.cron;
import cn.hutool.cron.task.CronTask;
import cn.hutool.cron.task.Task;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import cn.hutool.cron.task.Task;
/** /**
* 作业执行管理器<br> * 作业执行管理器<br>
* 负责管理作业的启动、停止等 * 负责管理作业的启动、停止等
@@ -47,7 +48,7 @@ public class TaskExecutorManager implements Serializable {
* @param task {@link Task} * @param task {@link Task}
* @return {@link TaskExecutor} * @return {@link TaskExecutor}
*/ */
public TaskExecutor spawnExecutor(Task task) { public TaskExecutor spawnExecutor(CronTask task) {
final TaskExecutor executor = new TaskExecutor(this.scheduler, task); final TaskExecutor executor = new TaskExecutor(this.scheduler, task);
synchronized (this.executors) { synchronized (this.executors) {
this.executors.add(executor); this.executors.add(executor);

View File

@@ -12,7 +12,13 @@ public class TaskLauncher implements Runnable{
private final Scheduler scheduler; private final Scheduler scheduler;
private final long millis; private final long millis;
/**
* 构造
*
* @param scheduler {@link Scheduler}
* @param millis 毫秒数
*/
public TaskLauncher(Scheduler scheduler, long millis) { public TaskLauncher(Scheduler scheduler, long millis) {
this.scheduler = scheduler; this.scheduler = scheduler;
this.millis = millis; this.millis = millis;
@@ -21,7 +27,7 @@ public class TaskLauncher implements Runnable{
@Override @Override
public void run() { public void run() {
//匹配秒部分由用户定义决定,始终不匹配年 //匹配秒部分由用户定义决定,始终不匹配年
scheduler.taskTable.executeTaskIfMatchInternal(millis); scheduler.taskTable.executeTaskIfMatch(this.scheduler, this.millis);
//结束通知 //结束通知
scheduler.taskLauncherManager.notifyLauncherCompleted(this); scheduler.taskLauncherManager.notifyLauncherCompleted(this);

View File

@@ -1,13 +1,13 @@
package cn.hutool.cron; package cn.hutool.cron;
import cn.hutool.cron.pattern.CronPattern; import cn.hutool.cron.pattern.CronPattern;
import cn.hutool.cron.task.CronTask;
import cn.hutool.cron.task.Task; import cn.hutool.cron.task.Task;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -22,24 +22,33 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
public class TaskTable implements Serializable { public class TaskTable implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final ReadWriteLock lock = new ReentrantReadWriteLock(); public static final int DEFAULT_CAPACITY = 10;
private final Scheduler scheduler; private final ReadWriteLock lock;
private final TimeZone timezone;
private final List<String> ids = new ArrayList<>(); private final List<String> ids;
private final List<CronPattern> patterns = new ArrayList<>(); private final List<CronPattern> patterns;
private final List<Task> tasks = new ArrayList<>(); private final List<Task> tasks;
private int size; private int size;
/** /**
* 构造 * 构造
*
* @param scheduler {@link Scheduler}
*/ */
public TaskTable(Scheduler scheduler) { public TaskTable() {
this.scheduler = scheduler; this(DEFAULT_CAPACITY);
this.timezone = scheduler.getTimeZone(); }
/**
* 构造
*
* @param initialCapacity 容量,即预估的最大任务数
*/
public TaskTable(int initialCapacity) {
lock = new ReentrantReadWriteLock();
ids = new ArrayList<>(initialCapacity);
patterns = new ArrayList<>(initialCapacity);
tasks = new ArrayList<>(initialCapacity);
} }
/** /**
@@ -246,13 +255,14 @@ public class TaskTable implements Serializable {
/** /**
* 如果时间匹配则执行相应的Task带读锁 * 如果时间匹配则执行相应的Task带读锁
* *
* @param scheduler {@link Scheduler}
* @param millis 时间毫秒 * @param millis 时间毫秒
*/ */
public void executeTaskIfMatch(long millis) { public void executeTaskIfMatch(Scheduler scheduler, long millis) {
final Lock readLock = lock.readLock(); final Lock readLock = lock.readLock();
readLock.lock(); readLock.lock();
try { try {
executeTaskIfMatchInternal(millis); executeTaskIfMatchInternal(scheduler, millis);
} finally { } finally {
readLock.unlock(); readLock.unlock();
} }
@@ -261,14 +271,15 @@ public class TaskTable implements Serializable {
/** /**
* 如果时间匹配则执行相应的Task无锁 * 如果时间匹配则执行相应的Task无锁
* *
* @param scheduler {@link Scheduler}
* @param millis 时间毫秒 * @param millis 时间毫秒
* @since 3.1.1 * @since 3.1.1
*/ */
protected void executeTaskIfMatchInternal(long millis) { protected void executeTaskIfMatchInternal(Scheduler scheduler, long millis) {
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
if (patterns.get(i).match(timezone, millis, this.scheduler.matchSecond)) { if (patterns.get(i).match(scheduler.config.timezone, millis, scheduler.config.matchSecond)) {
this.scheduler.taskExecutorManager.spawnExecutor(tasks.get(i)); scheduler.taskExecutorManager.spawnExecutor(new CronTask(ids.get(i), patterns.get(i), tasks.get(i)));
} }
} }
} }
} }

View File

@@ -65,8 +65,8 @@ public class TaskListenerManager implements Serializable {
public void notifyTaskSucceeded(TaskExecutor executor) { public void notifyTaskSucceeded(TaskExecutor executor) {
synchronized (listeners) { synchronized (listeners) {
int size = listeners.size(); int size = listeners.size();
for (TaskListener listenerl : listeners) { for (TaskListener listener : listeners) {
listenerl.onSucceeded(executor); listener.onSucceeded(executor);
} }
} }
} }

View File

@@ -0,0 +1,70 @@
package cn.hutool.cron.task;
import cn.hutool.cron.pattern.CronPattern;
/**
* 定时作业此类除了定义了作业也定义了作业的执行周期以及ID。
*
* @author looly
* @since 5.4.7
*/
public class CronTask implements Task{
private final String id;
private CronPattern pattern;
private final Task task;
/**
* 构造
* @param id ID
* @param pattern 表达式
* @param task 作业
*/
public CronTask(String id, CronPattern pattern, Task task) {
this.id = id;
this.pattern = pattern;
this.task = task;
}
@Override
public void execute() {
task.execute();
}
/**
* 获取作业ID
*
* @return 作业ID
*/
public String getId() {
return id;
}
/**
* 获取表达式
*
* @return 表达式
*/
public CronPattern getPattern() {
return pattern;
}
/**
* 设置新的定时表达式
* @param pattern 表达式
* @return this
*/
public CronTask setPattern(CronPattern pattern){
this.pattern = pattern;
return this;
}
/**
* 获取原始作业
*
* @return 作业
*/
public Task getRaw(){
return this.task;
}
}

View File

@@ -10,6 +10,7 @@ package cn.hutool.cron.task;
* *
* @author Looly * @author Looly
*/ */
@FunctionalInterface
public interface Task { public interface Task {
/** /**

View File

@@ -22,6 +22,9 @@ public class CronTest {
// 支持秒级别定时任务 // 支持秒级别定时任务
CronUtil.setMatchSecond(true); CronUtil.setMatchSecond(true);
CronUtil.start(); CronUtil.start();
ThreadUtil.waitForDie();
Console.log("Exit.");
} }
@Test @Test
@@ -32,27 +35,27 @@ public class CronTest {
CronUtil.getScheduler().setDaemon(false); CronUtil.getScheduler().setDaemon(false);
CronUtil.start(); CronUtil.start();
ThreadUtil.sleep(3000); ThreadUtil.waitForDie();
CronUtil.stop(); CronUtil.stop();
} }
@Test @Test
@Ignore @Ignore
public void cronTest2() { public void cronWithListenerTest() {
CronUtil.getScheduler().addListener(new TaskListener() { CronUtil.getScheduler().addListener(new TaskListener() {
@Override @Override
public void onStart(TaskExecutor executor) { public void onStart(TaskExecutor executor) {
Console.log("Listen task start!"); Console.log("Found task:[{}] start!", executor.getCronTask().getId());
} }
@Override @Override
public void onSucceeded(TaskExecutor executor) { public void onSucceeded(TaskExecutor executor) {
Console.log("Found task:[{}] success!", executor.getCronTask().getId());
} }
@Override @Override
public void onFailed(TaskExecutor executor, Throwable exception) { public void onFailed(TaskExecutor executor, Throwable exception) {
Console.error("Found task:[{}] failed!", executor.getCronTask().getId());
} }
}); });
@@ -65,7 +68,7 @@ public class CronTest {
} }
@Test @Test
// @Ignore @Ignore
public void addAndRemoveTest() { public void addAndRemoveTest() {
String id = CronUtil.schedule("*/2 * * * * *", (Runnable) () -> Console.log("task running : 2s")); String id = CronUtil.schedule("*/2 * * * * *", (Runnable) () -> Console.log("task running : 2s"));