import org.quartz.TriggerUtils;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.CronTask;
import java.text.ParseException;
import java.time.Duration;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
/**
* 所用依赖:
* <dependencies>
* <dependency>
* <groupId>org.springframework.boot</groupId>
* <artifactId>spring-boot-starter-web</artifactId>
* <version>2.1.8.RELEASE</version>
* </dependency>
* <dependency>
* <groupId>org.quartz-scheduler</groupId>
* <artifactId>quartz</artifactId>
* <version>2.3.2</version>
* </dependency>
* </dependencies>
*/
/**
* 解决crontab定时任务只能按照固定时间开始执行,不能实现立即执行的问题。
* 问题描述:
** 1. crontab表达式比如 0 0/10 * * * ?
* 2. 当前时间是2021-07-20 17:58:00, 那么它接下来的执行时间
* 只能是
* 2021-07-20 18:00:00
* 2021-07-20 18:10:00
* 2021-07-20 18:20:00
* 2021-07-20 18:30:00
* 而无法是
* 2021-07-20 17:58:00(第一次为立即执行)
* 2021-07-20 18:08:00
* 2021-07-20 18:18:00
* 2021-07-20 18:28:00
* 3. 这篇提供的代码能做到 执行时间为
* 2021-07-20 17:58:00(第一次为立即执行)
* 2021-07-20 18:10:00(第二次会根据周期间隔做修正,注意这里跳过了2021-07-20 18:00:00这个时刻)
* 2021-07-20 18:20:00
* 2021-07-20 18:30:00
* "根据周期间隔做修正"的意思就是说,假如当前时间是2021-07-20 17:52:00,那么执行时间就是
* 2021-07-20 17:52:00(第一次为立即执行)
* 2021-07-20 18:00:00(这一次就没有跳过18:00这个时刻)
* 2021-07-20 18:10:00
* 2021-07-20 18:20:00
* 2021-07-20 18:30:00
*
*/
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
System.out.println(new Date());
Task task = new Task();
task.start();
}
}
class Task{
public void start() {
String cron = "0 */1 * * * ?";
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(1);
taskScheduler.initialize();
CronTask discoverTask = new CronTask(new Executor(), cron);
Trigger tmp = discoverTask.getTrigger();
CronTriggerImpl cronTriggerImpl = new CronTriggerImpl();
try {
cronTriggerImpl.setCronExpression(cron);
} catch (ParseException e) {
e.printStackTrace();
}
List<Date> dates = TriggerUtils.computeFireTimes(cronTriggerImpl, null, 3);
for (int i = 0; i < dates.size(); i++) {
System.out.println("接下来第" + (i + 1) + "次执行时间" + dates.get(i));
}
Duration between = Duration.between(dates.get(0).toInstant(), dates.get(1).toInstant()).abs();
long step = between.getSeconds();
if (step == 0) {
step = 1;
}
System.out.println("执行周期为" + step + "秒");
long finalStep = step;
Trigger trigger = new Trigger() {
AtomicLong count = new AtomicLong(1);
double factor = 0.75; // 调节因子
Date firstRunDate;
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
if (count.get() == 1) { // 第一次用当前时间作为触发时间
firstRunDate = new Date();
count.incrementAndGet();
return firstRunDate;
}
if (count.get() == 2) { // 第二次判断是否如期执行。如果该次时间和第一次执行时间"很近", 就忽略该次。
Date current = tmp.nextExecutionTime(triggerContext);
long past = Duration.between(firstRunDate.toInstant(), current.toInstant()).abs().getSeconds();
count.incrementAndGet();
if (past > (factor * finalStep)) {
System.out.println("第一次执行时间距下一次执行时间间隔" + past + "秒, 接近一个周期,run!");
return current;
}
System.out.println("第一次执行时间距下一次执行时间间隔" + past + "秒, 间隔太短,不run!");
return null;
}
return tmp.nextExecutionTime(triggerContext);
}
};
taskScheduler.schedule(discoverTask.getRunnable(), trigger);
}
public class Executor implements Runnable {
@Override
public void run() {
System.out.println("运行中,当前时间:" + new Date());
}
}
}