mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-12-07 01:28:34 +08:00
修复CronTimer在任务非常多时,追赶系统时间导致遗漏任务的问题(issue#IB49EF@Gitee)
This commit is contained in:
parent
9dab4f260a
commit
1489063bbd
@ -39,6 +39,7 @@
|
|||||||
* 【core 】 修复`StrBuilder`charAt越界判断错误(pr#4094@Github)
|
* 【core 】 修复`StrBuilder`charAt越界判断错误(pr#4094@Github)
|
||||||
* 【dfa 】 修复`WordTree.addWord`末尾为特殊字符导致的无法匹配问题(pr#4092@Github)
|
* 【dfa 】 修复`WordTree.addWord`末尾为特殊字符导致的无法匹配问题(pr#4092@Github)
|
||||||
* 【core 】 修复`ServiceLoaderUtil.loadFirstAvailable`在JDK24+后未捕获异常导致的报错问题(pr#4098@Github)
|
* 【core 】 修复`ServiceLoaderUtil.loadFirstAvailable`在JDK24+后未捕获异常导致的报错问题(pr#4098@Github)
|
||||||
|
* 【cron 】 修复`CronTimer`在任务非常多时,追赶系统时间导致遗漏任务的问题(issue#IB49EF@Gitee)
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
# 5.8.40(2025-08-26)
|
# 5.8.40(2025-08-26)
|
||||||
|
|||||||
@ -38,27 +38,40 @@ public class CronTimer extends Thread implements Serializable {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
final long timerUnit = this.scheduler.config.matchSecond ? TIMER_UNIT_SECOND : TIMER_UNIT_MINUTE;
|
final long timerUnit = this.scheduler.config.matchSecond ? TIMER_UNIT_SECOND : TIMER_UNIT_MINUTE;
|
||||||
|
final long doubleTimeUnit = 2 * timerUnit;
|
||||||
|
|
||||||
long thisTime = System.currentTimeMillis();
|
long thisTime = System.currentTimeMillis();
|
||||||
long nextTime;
|
long nextTime;
|
||||||
long sleep;
|
long sleep;
|
||||||
while(false == isStop){
|
while(false == isStop){
|
||||||
|
spawnLauncher(thisTime);
|
||||||
|
|
||||||
//下一时间计算是按照上一个执行点开始时间计算的
|
//下一时间计算是按照上一个执行点开始时间计算的
|
||||||
//此处除以定时单位是为了清零单位以下部分,例如单位是分则秒和毫秒清零
|
//此处除以定时单位是为了清零单位以下部分,例如单位是分则秒和毫秒清零
|
||||||
nextTime = ((thisTime / timerUnit) + 1) * timerUnit;
|
nextTime = ((thisTime / timerUnit) + 1) * timerUnit;
|
||||||
sleep = nextTime - System.currentTimeMillis();
|
sleep = nextTime - System.currentTimeMillis();
|
||||||
if(isValidSleepMillis(sleep, timerUnit)){
|
if(sleep < 0){
|
||||||
if (false == ThreadUtil.safeSleep(sleep)) {
|
// 可能循环执行慢导致时间点跟不上系统时间,追赶系统时间并执行中间差异的时间点(issue#IB49EF@Gitee)
|
||||||
//等待直到下一个时间点,如果被中断直接退出Timer
|
thisTime = System.currentTimeMillis();
|
||||||
|
while(nextTime <= thisTime){
|
||||||
|
// 追赶系统时间并运行执行点
|
||||||
|
spawnLauncher(nextTime);
|
||||||
|
nextTime = ((thisTime / timerUnit) + 1) * timerUnit;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else if(sleep > doubleTimeUnit){
|
||||||
|
// 时间回退,可能用户回拨了时间或自动校准了时间,重新计算(issue#1224@Github)
|
||||||
|
thisTime = System.currentTimeMillis();
|
||||||
|
continue;
|
||||||
|
} else if (false == ThreadUtil.safeSleep(sleep)) {
|
||||||
|
//等待直到下一个时间点,如果被用户中断直接退出Timer
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
//执行点,时间记录为执行开始的时间,而非结束时间
|
|
||||||
thisTime = System.currentTimeMillis();
|
// issue#3460 采用叠加方式,确保正好是1分钟或1秒,避免sleep晚醒问题
|
||||||
spawnLauncher(thisTime);
|
// 此处无需校验,因为每次循环都是sleep与上触发点的时间差。
|
||||||
} else{
|
// 当上一次晚醒后,本次会减少sleep时间,保证误差在一个unit内,并不断修正。
|
||||||
// 非正常时间重新计算(issue#1224@Github)
|
thisTime = nextTime;
|
||||||
thisTime = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
log.debug("Hutool-cron timer stopped.");
|
log.debug("Hutool-cron timer stopped.");
|
||||||
}
|
}
|
||||||
@ -76,24 +89,7 @@ public class CronTimer extends Thread implements Serializable {
|
|||||||
* @param millis 当前时间
|
* @param millis 当前时间
|
||||||
*/
|
*/
|
||||||
private void spawnLauncher(final long millis){
|
private void spawnLauncher(final long millis){
|
||||||
|
//Console.log(millis / 1000, System.currentTimeMillis() / 1000);
|
||||||
this.scheduler.taskLauncherManager.spawnLauncher(millis);
|
this.scheduler.taskLauncherManager.spawnLauncher(millis);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查是否为有效的sleep毫秒数,包括:
|
|
||||||
* <pre>
|
|
||||||
* 1. 是否>0,防止用户向未来调整时间
|
|
||||||
* 1. 是否<两倍的间隔单位,防止用户向历史调整时间
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* @param millis 毫秒数
|
|
||||||
* @param timerUnit 定时单位,为秒或者分的毫秒值
|
|
||||||
* @return 是否为有效的sleep毫秒数
|
|
||||||
* @since 5.3.2
|
|
||||||
*/
|
|
||||||
private static boolean isValidSleepMillis(long millis, long timerUnit){
|
|
||||||
return millis > 0 &&
|
|
||||||
// 防止用户向前调整时间导致的长时间sleep
|
|
||||||
millis < (2 * timerUnit);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package cn.hutool.cron.demo;
|
|||||||
import cn.hutool.core.lang.Console;
|
import cn.hutool.core.lang.Console;
|
||||||
import cn.hutool.core.thread.ThreadUtil;
|
import cn.hutool.core.thread.ThreadUtil;
|
||||||
import cn.hutool.cron.CronUtil;
|
import cn.hutool.cron.CronUtil;
|
||||||
|
import cn.hutool.cron.Scheduler;
|
||||||
import cn.hutool.cron.TaskExecutor;
|
import cn.hutool.cron.TaskExecutor;
|
||||||
import cn.hutool.cron.listener.TaskListener;
|
import cn.hutool.cron.listener.TaskListener;
|
||||||
import cn.hutool.cron.task.Task;
|
import cn.hutool.cron.task.Task;
|
||||||
@ -14,6 +15,19 @@ import org.junit.jupiter.api.Test;
|
|||||||
*/
|
*/
|
||||||
public class CronTest {
|
public class CronTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Disabled
|
||||||
|
void emptyScheduleTest() {
|
||||||
|
final Scheduler scheduler = new Scheduler();
|
||||||
|
// 支持秒级别定时任务
|
||||||
|
scheduler.setMatchSecond(true);
|
||||||
|
|
||||||
|
scheduler.start();
|
||||||
|
|
||||||
|
ThreadUtil.waitForDie();
|
||||||
|
Console.log("Exit.");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Disabled
|
@Disabled
|
||||||
public void customCronTest() {
|
public void customCronTest() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user