【Java】手动定时任务工具

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.ScheduledFuture;


/**
 * 定时任务工具
 *
 * @author zhangxuetu
 * @date 2023-01-09
 */
@Component
@Slf4j
public class ScheduleUtil {
    /*** ID对应的定时任务数据 */
    private static final Map<Object, Data> DATA_MAP = new HashMap<>();

    /*** 任务ID */
    private static long id = 0L;
    /*** 定时任务线程池 */
    private static ThreadPoolTaskScheduler threadPoolTaskScheduler = null;

    /**
     * 获取定时任务线程池对象
     */
    private static ThreadPoolTaskScheduler getThreadPoolTaskScheduler() {
        if (threadPoolTaskScheduler == null) {
            threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
            threadPoolTaskScheduler.initialize();
        }
        return threadPoolTaskScheduler;
    }

    /**
     * 执行任务的数据
     */
    private static class Data {
        private final Object id;
        private final ScheduledFuture<?> scheduledFuture;
        private final String cron;
        private final Runnable runnable;

        public Data(Object id, ScheduledFuture<?> scheduledFuture, String cron, Runnable runnable) {
            this.id = id;
            this.scheduledFuture = scheduledFuture;
            this.cron = cron;
            this.runnable = runnable;
        }

        public Object getId() {
            return id;
        }

        public ScheduledFuture<?> getScheduledFuture() {
            return scheduledFuture;
        }

        public String getCron() {
            return cron;
        }

        public Runnable getRunnable() {
            return runnable;
        }
    }

    /**
     * 获取这个ID的定时任务数据
     *
     * @param id ID值
     * @return 返回定时任务数据对象
     */
    private static <T> Data getData(T id) {
        return DATA_MAP.get(id);
    }

    /**
     * 是否有这个定时任务
     *
     * @param id  ID值
     * @param <T> ID值类型
     * @return 返回是否有这个定时任务
     */
    public static <T> boolean hasScheduledFuture(T id) {
        return DATA_MAP.containsKey(id);
    }

    /**
     * 添加自定义定时任务
     *
     * @param cron     定时任务表达式
     * @param runnable 执行功能
     * @return 返回这次定时任务的 ID 值
     */
    public static long addTask(String cron, Runnable runnable) throws IllegalArgumentException {
        // 直到没有相同的 ID 的时候才添加
        do {
            id++;
        } while (DATA_MAP.containsKey(id));
        return addTask(id, cron, runnable);
    }

    /**
     * 添加自定义定时任务
     *
     * @param id       自定义设置的 ID 值
     * @param cron     定时任务表达式
     * @param runnable 执行功能
     * @throws IllegalArgumentException 参数异常
     */
    public static <T> T addTask(T id, String cron, Runnable runnable) throws IllegalArgumentException {
        if (id == null) {
            throw new IllegalArgumentException("ID 值不能为 null!");
        }
        if (hasScheduledFuture(id)) {
            throw new IllegalArgumentException("已添加过这个 ID 的定时任务!");
        }
        log.info("新增定时任务:id = {}, cron = {}", id, cron);
        ScheduledFuture<?> schedule = getThreadPoolTaskScheduler().schedule(runnable, triggerContext -> new CronTrigger(cron).nextExecutionTime(triggerContext));
        Data data = new Data(id, schedule, cron, runnable);
        DATA_MAP.put(id, data);
        return id;
    }

    /**
     * 停止定时任务
     *
     * @param id 定时任务的ID
     * @return 返回是否停止成功,如果不成功则代表没有这个任务
     */
    public static <T> boolean stop(T id) throws NullPointerException {
        if (hasScheduledFuture(id)) {
            ScheduledFuture<?> scheduledFuture = getData(id).getScheduledFuture();
            scheduledFuture.cancel(true);
            DATA_MAP.remove(id);
            log.info("移除定时任务:id = {}", id);
            return true;
        }
        return false;
    }

    /**
     * 更新定时任务
     *
     * @param id   定时任务的ID
     * @param cron 定时任务表达式
     * @param <T>  ID值的类型
     */
    public static <T> boolean update(T id, String cron) {
        if (hasScheduledFuture(id)) {
            Runnable runnable = getData(id).getRunnable();
            stop(id);
            addTask(id, cron, runnable);
            return true;
        }
        return false;
    }

    /**
     * 更新定时任务
     *
     * @param id       定时任务的ID
     * @param runnable 执行的功能
     * @param <T>      ID值的类型
     */
    public static <T> boolean update(T id, Runnable runnable) {
        if (hasScheduledFuture(id)) {
            String cron = getData(id).getCron();
            stop(id);
            addTask(id, cron, runnable);
            return true;
        }
        return false;
    }

    /**
     * 更新定时任务
     *
     * @param id       定时任务的ID
     * @param cron     定时任务表达式
     * @param runnable 运行的方法
     * @param <T>      ID值的类型
     */
    public static <T> boolean update(T id, String cron, Runnable runnable) {
        if (hasScheduledFuture(id)) {
            stop(id);
            addTask(id, cron, runnable);
            return true;
        }
        return false;
    }


    public static String cron(String time, List<? extends Comparable<?>> weeks) {
        return cron(Arrays.asList(time), weeks);
    }

    /**
     * 转为定时任务的表达式
     *
     * @param times 时间格式字符串或LocalTime列表(格式:时:分:秒)
     * @param weeks 周列表
     * @return 返回定时任务的表达式
     */
    public static String cron(List<?> times, List<? extends Comparable<?>> weeks) {
        if (times == null || times.isEmpty()) {
            throw new IllegalArgumentException("times 参数为空!");
        }
        if (weeks == null || weeks.isEmpty()) {
            throw new IllegalArgumentException("weeks 参数为空!");
        }

        List<String> seconds = new ArrayList<>();
        List<String> minutes = new ArrayList<>();
        List<String> hours = new ArrayList<>();
        for (Object time : times) {
            LocalTime localTime = LocalTime.parse(time.toString());
            seconds.add(String.valueOf(localTime.getSecond()));
            minutes.add(String.valueOf(localTime.getMinute()));
            hours.add(String.valueOf(localTime.getHour()));
        }
        return String.format("%s %s %s ? * %s",
                String.join(",", seconds),
                String.join(",", minutes),
                String.join(",", hours),
                String.join(",", weeks.toArray(new String[0]))
        );
    }

}

发表评论