解決crontab定時任務只能按照固定時間開始執行,不能實現立即執行的問題


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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM