Skip to content

❤ 静态任务

[!quote] 静态任务 静态任务 是在应用启动时就被加载和注册的定时任务,调度时间在应用启动时就被计算。它们是基于配置文件或代码中的 @Scheduled 注解声明的

java
@Scheduled(cron="0 0 2 * * ?") //每天凌晨2点执行
public void doStaticTask() {
    // 执行静态定时任务的代码
}

💛 操作步骤

  • 在类上加入 @EnableScheduling开启定时任务
java
@EnableScheduling
public class ChatBotSchedule {……}
  • 使用 @Scheduled,对任务设置执行时间。有三种配置执行时间的方式:
    • cron 表达式
    • fixedRate 自上一次执行开始之后多长时间执行【单位是毫秒
    • fixedRateStringfixedRate 相同,但是支持占位符
    • fixedDelay 自上一次执行结束之后多长时间执行【单位是毫秒
    • fixedDelayString
    • initialDelay 首次执行要延迟多长时间【单位毫秒
    • initialDelayString
java
@Scheduled(cron = "0/20 * * * * ?")  

@Scheduled(fixedRate = 1 * 60 * 1000)  

// 配置文件中
interval=2000
@Scheduled(fixedRateStirng="${interval}")
public void RunSchedule() {……}

💛 @Scheduled

💙 Cron 表达式

cron= "秒 分 时 日 月 周几"

`周` 和 `日` 不能同时存在【一个设置了,另一个就要设置为 `?` 】

  • 取值
    • 0-59
    • 0-59
    • 0-23
    • 1-31
    • 1-12
    • 0-7【0/7 是 周日,1-6 是周一到周六】
  • 通配符
    • # 每月的第几个周几【比如 1#2,表示每月的第 2 个星期天】【==只用在周==】
    • L 每月的最后一个什么【比如,在日上 3L 表示这个月的倒数第三天,周上 3L 表示这个月的最后一个周四【==只用在周,或日==】【Spring 中不支持】
    • * 所有值,表示每秒,每月,每日
    • ? 不指定值【比如已经设置了每日,那具体周几就不用设置了,那么就设为 ?
    • - 表示区间【比如在周上设置 3-5,就表示周三到周五都会执行】
    • , 多个并列【比如日的 2,13,14,就是 2 号,13 号,14 号都会执行】
    • / 间隔【比如秒的 2/3,从第 2 秒开始,每三秒触发一次】
java
0/2 * * * * ?   从第 0 秒开始,每 2 秒触发

0 0/5 14 * * ?    在每天下午 2 点到下午 2:55 期间的每5分钟触发【2 点会触发,2:55 也会触发】

0 15 10 ? * 6#3   每月的第三个星期五上午 10:15 触发

0 0 2 1 * ?   每月的 1 号,凌晨 2 点触发

0 * 14 * * ?    每天下午 2 点到下午 2:59 的每 1 分钟触发

0 0-5 14 * * ?    每天下午 2 点到下午 2:05 的每 1 分钟触发

💙 fixedRate

fixedRate 的时间单位是毫秒

java
@Scheduled(fixedRate = 6300000) // 1h 45min

💙 fixedRateString

java
// 配置文件
interval=2000

// 注解引用
@Scheduled(fixedRateStirng="${interval}")

💙 initialDelay

java
// 系统启动之后立马会执行一次
@Scheduled(fixedRate = 6300000, initialDelay = 0)

💛 定时调度异步线程池

[!quote] 定时调度异步线程池 定时调度异步线程池 既用来调度任务,也用来异步执行任务

  • 如果不额外创建线程池,那所有的定时任务都会使用默认的线程池(大小为 1)
  • 配置好的线程池,在被 @Scheduled 自动应用
  • 当任务开始调度时,会把任务放进队列中,到达指定时间后,再从使用线程来执行该任务
java
/**
 * 异步定时任务配置类
 */
@EnableScheduling
@Configuration
public class AsyncSchedulingConfig {
    @Bean(name = "myScheduledThreadPool")
    public ThreadPoolTaskScheduler myScheduledThreadPool() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(20); // 设置线程池大小
        scheduler.setThreadNamePrefix("MyScheduledThreadPool - ");
        scheduler.initialize();

        return scheduler;
    }
}

❤ 动态任务

动态任务是在运行时动态添加和移除的定时任务,而不需要重新启动应用

💛 动态 Cron

java
@Component
public class DynamicTaskScheduler {

    private ScheduledFuture<?> currentTask;
    
    @Resource
    private ThreadPoolTaskScheduler scheduler;

    public void scheduleTask(String cron) {
        // 如果有正在运行的任务,取消它
        if (currentTask != null && !currentTask.isCancelled()) {
            currentTask.cancel(true);
        }

        // 调度新的任务
        currentTask = scheduler.schedule(() -> {
            // 执行动态定时任务的代码
        }, new CronTrigger(cron));
    }
}

💛 动态延时任务

  • ScheduledExecutorService 用于管理定时任务的执行
  • ScheduledFuture 持有定时任务的执行状态和结果,用于取消任务,或检查任务是否完成,在调用 scheduler.schedule() 时立即生成
    • isCancelled
      • false 任务正常进行,结束,执行中,执行报错
      • true 只有当我们手动结束任务,才会返回
    • cancel(Boolean)
      • 传 true - 会中断正在执行的任务
      • 传 false - 不会中断正在执行的任务,但会取消计划任务(后续不再执行)

在开启调度任务时,不会占用线程,只有在延时结束实际执行时才会占用线程,任务执行完毕,立马释放线程资源

java
/**
 * 工时审批通知定时任务
 */
@Configuration
public class WHApprovalNotifyTask {

    // 这里要提前定义好一个动态线程池
	@Resource
	private ThreadPoolTaskScheduler taskScheduler;

    private ScheduledFuture<?> scheduledFuture;

    /** 开启定时任务,有人登记工时,就调用该方法 */
    public void scheduleTask(Integer delay) {
	    // 任务在delay小时后执行
		scheduledFuture = taskScheduler.schedule(  
		        () -> {  
					// 判断取消任务
					if (scheduledFuture != null) scheduledFuture.cancel(false);
					// 执行任务
					notifyByUserIds();
		        },  
		        Instant.now().plus(delay, ChronoUnit.HOURS)  
		);
    }

}