使用ScheduledExecutorService线程池手动动态控制定时任务

时间:2021-07-28
本文章向大家介绍使用ScheduledExecutorService线程池手动动态控制定时任务,主要包括使用ScheduledExecutorService线程池手动动态控制定时任务使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

背景

在日常开发过程中,使用定时任务去执行一些业务逻辑是很常见的一种场景。比如定时发送短信,邮件,电商系统的定时自动收货、定时上下架功能等等。

一般实现定时任务有以下几种方案:

JDK自带 

  • JDK自带的Timer:这是java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行,一般用的较少。
  • JDK1.5+ 新增的ScheduledExecutorService:是基于线程池设计的定时任务类,每个调度任务都会分配到线程池中的一个线程去执行,也就是说,任务是并发执行,互不影响。

第三方框架 

  使用 Quartz、elastic-job、xxl-job 等开源第三方定时任务框架,适合分布式项目应用。不过配置起来稍显复杂,不太易上手。
Spring Task 

  Spring3.0以后自带的task,可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多。使用 Spring 提供的一个注解 @Schedule即可,开发简单,使用比较方便。

本文主要向大家介绍使用ScheduledExecutorService操作定时任务。

使用

1.启动类添加 @EnableScheduling 注解

2.创建定时任务类

ScheduleController.java

/** 
 * <p>
 *  接口控制定时任务开始和停止
 * </p>
 *
 * @className ScheduleController
 * @author Sue
 * @date 2021/7/28
 **/
@RestController
@RequestMapping("/task")
public class ScheduleController {

    ScheduleTaskService scheduleTaskService;

    public ScheduleController(ScheduleTaskService scheduleTaskService) {
        this.scheduleTaskService = scheduleTaskService;
    }

    @PostMapping("/startCron")
    public R startCron() {
        scheduleTaskService.startCorn();
        return R.ok("定时任务启动成功!");
    }

    @PostMapping("/stopCron")
    public R stopCron() {
        scheduleTaskService.stopCorn();
        return R.ok("定时任务关闭成功!");
    }
    
}

ScheduleTaskService.java

/**
 * <p>
 *  定时任务
 * </p>
 *
 * @className ThreadPoolTaskSchedulerService
 * @author Sue
 * @create 2021/7/21 
 **/
public interface ScheduleTaskService {
    /**
     * <p>
     *  开始定时任务
     * </p>
     *
     * @return boolean
     * @author Sue
     * @date 2021/7/21
     */
    boolean startCorn();

    /**
     * <p>
     *  关闭定时任务
     * </p>
     *
     * @return boolean
     * @author Sue
     * @date 2021/7/21
     */
    boolean stopCorn();
}

ScheduleTaskServiceImpl.java

/**
 * <p>
 *  定时任务
 * </p>
 *
 * @className ThreadPoolTaskSchedulerServiceImpl
 * @author Sue
 * @create 2021/7/21 
 **/
@Slf4j
@Service
public class ScheduleTaskServiceImpl implements ScheduleTaskService {

    private ScheduledFuture<?> future;

    ThreadPoolTaskScheduler threadPoolTaskScheduler;

    public ScheduleTaskServiceImpl(ThreadPoolTaskScheduler threadPoolTaskScheduler) {
        this.threadPoolTaskScheduler = threadPoolTaskScheduler;
    }

    @Override
    public boolean startCorn() {
        if (future != null) {
            future.cancel(true);
            log.info("定时任务已停止");
        }
        //每10秒执行一次
        String cornConfig = "0/10 * * * * *";
        future = threadPoolTaskScheduler.schedule(new ScheduledTaskRunnable(), new CronTrigger(cornConfig));
        log.info("定时任务开启");
        return false;
    }

    @Override
    public boolean stopCorn() {
        if (future != null) {
            future.cancel(true);
            log.info("定时任务已停止");
        }
        return false;
    }

    static class ScheduledTaskRunnable implements Runnable {
        @Override
        public void run() {
            //需要执行的业务逻辑
            log.info("定时任务开始执行,每10秒执行一次,当前时间{}", LocalDateTimeUtil.format(LocalDateTimeUtil.now(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        }

    }
}

3.项目启动后,调用接口,控制定时任务的启动和停止

测试

调用startCorn接口,可以看出定时任务成功开启

 调用stopCorn接口,停止定时任务

补充

corn表达式的使用

cron 表达式是一个字符串,该字符串由 6 个空格分为 7 个域,每一个域代表一个时间含义。 通常定义 “年” 的部分可以省略,实际常用的 Cron 表达式由前 6 部分组成。格式如下:

[秒] [分] [时] [日] [月] [周] [年]
Seconds  Minutes  Hours   Day-of-Month  Month   Day-of-Week    Year (optional field)

  需要说明的是,Cron 表达式中,“周” 是从周日开始计算的。“周” 域上的 1 表示的是周日,7 表示周六。

通配符说明

  • * 表示所有值. 例如:在分的字段上设置 “*”,表示每一分钟都会触发
  • ? 表示不指定值。使用的场景为不需要关心当前设置这个字段的值。例如:要在每月的10号触发一个操作,但不关心是周几,所以需要周位置的那个字段设置为"?" 具体设置为 0 0 0 10 * ?
  • - 表示区间。例如 在小时上设置 “10-12”,表示 10,11,12点都会触发。
  • , 表示指定多个值,例如在周字段上设置 “MON,WED,FRI” 表示周一,周三和周五触发
  • / 用于递增触发。如在秒上面设置"5/15" 表示从5秒开始,每增15秒触发(5,20,35,50)。 在月字段上设置’1/3’所示每月1号开始,每隔三天触发一次。
  • L 表示最后的意思。在日字段设置上,表示当月的最后一天(依据当前月份,如果是二月还会依据是否是润年[leap]), 在周字段上表示星期六,相当于"7"或"SAT"。如果在"L"前加上数字,则表示该数据的最后一个。例如在周字段上设置"6L"这样的格式,则表示“本月最后一个星期五"
  • W 表示离指定日期的最近那个工作日(周一至周五). 例如在日字段上设置"15W",表示离每月15号最近的那个工作日触发。如果15号正好是周六,则找最近的周五(14号)触发, 如果15号是周未,则找最近的下周一(16号)触发.如果15号正好在工作日(周一至周五),则就在该天触发。如果指定格式为 “1W”,它则表示每月1号往后最近的工作日触发。如果1号正是周六,则将在3号下周一触发。(注,“W"前只能设置具体的数字,不允许区间”-").
  • # 序号(表示每月的第几个周几),例如在周字段上设置"6#3"表示在每月的第三个周六.注意如果指定"#5",正好第五周没有周六,则不会触发该配置(用在母亲节和父亲节再合适不过了) ;

提示:
'L’和 'W’可以组合使用。如果在日字段上设置"LW",则表示在本月的最后一个工作日触发;
周字段的设置,若使用英文字母是不区分大小写的,即MON 与mon相同;

例子

  • "0 */1 * * * ?" 每隔 1 分钟执行一次
  • "0 24,30 * * * ?" 在24分,30分执行一次
  • "0 0 10,14,16 * * ?" 每天上午10点,下午2点,4点
  • "0 0/30 9-17 * * ?" 朝九晚五工作时间内每半小时
  • "0 0 12 ? * WED" 表示每个星期三中午12点
  • "0 0 12 * * ?" 每天中午12点触发
  • "0 15 10 ? * *" 每天上午10:15触发
  • "0 15 10 * * ?" 每天上午10:15触发
  • "0 15 10 * * ? *" 每天上午10:15触发
  • "0 15 10 * * ? 2005" 2005年的每天上午10:15触发
  • "0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发
  • "0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发
  • "0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
  • "0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发
  • "0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发
  • "0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发
  • "0 15 10 15 * ?" 每月15日上午10:15触发
  • "0 15 10 L * ?" 每月最后一日的上午10:15触发
  • "0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发
  • "0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发
  • "0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发

原文地址:https://www.cnblogs.com/sueyyyy/p/15069735.html