• 在SpringBoot中如何正确使用定时任务
  • 发布于 2个月前
  • 277 热度
    0 评论
  • 黄月英
  • 0 粉丝 41 篇博客
  •   
创建方式
在不用引入spring-boot-starter-quartz依赖下,SpringBoot创建定时任务,目前主要有以下两种实现方式:
1.基于注解@Scheduled

2.基于接口SchedulingConfigurer


方式一:
介绍
@Scheduled是Spring框架中的一个注解,用于定时执行任务。它可以用于定时执行一些简单的任务,如发送邮件、备份数据库等,也可以用于定时执行一些复杂的任务,如数据分析、定时检查系统健康状况等。@Scheduled可以与以下三个注解配合使用:
@Scheduled(fixedRate = 5000):表示每隔5秒执行一次任务。
@Scheduled(cron = "0 0 12 * * ?"):表示在每天中午12点执行任务。
@Scheduled(initialDelay = 1000, fixedDelay = 5000):表示在启动后1秒开始执行任务,每隔5秒执行一次任务。
fixedDelay:表示前一次任务执行完成后,延时多久再执行下一次任务。
fixedRate:表示每隔固定时间执行一次任务

总之,@Scheduled是一个非常方便的定时执行任务的工具,可以大大提高开发效率。

原理
底层使用的是Spring的任务调度线程池ThreadPoolTaskScheduler,其底层使用的为JDK自带的ScheduledExecutorService(其实现类为ScheduledThreadPoolExecutor)图片注意:默认线程池是一个线程,即多任务调度会串行执行

简单实现
启动类需要开启定时任务@EnableScheduling
@Scheduled(cron = "*/2 * * * * ?")
public void testA() throws InterruptedException {
    log.info("testA 执行==============");
    Thread.sleep(5000);
}

@Scheduled(cron = "*/2 * * * * ?")
public void testB() throws InterruptedException {
    log.info("testB 执行==============");
    Thread.sleep(5000);
}
执行结果
2023-03-20 09:15:46.007  INFO 64180 --- [   scheduling-1] org.example.schedu.TestJob               : testA 执行==============
2023-03-20 09:15:51.009  INFO 64180 --- [   scheduling-1] org.example.schedu.TestJob               : testB 执行==============
2023-03-20 09:15:56.015  INFO 64180 --- [   scheduling-1] org.example.schedu.TestJob               : testA 执行==============
2023-03-20 09:16:01.019  INFO 64180 --- [   scheduling-1] org.example.schedu.TestJob               : testB 执行==============
2023-03-20 09:16:06.028  INFO 64180 --- [   scheduling-1] org.example.schedu.TestJob               : testA 执行==============
2023-03-20 09:16:11.038  INFO 64180 --- [   scheduling-1] org.example.schedu.TestJob               : testB 执行==============
可以明显看出两个任务是串行执行,后面会介绍如何实现多任务并行执行

方式二:
介绍
schedulingconfigurer是Spring框架中的一个接口,用于配置任务调度器的相关参数。该接口提供了多个方法,用于配置不同的参数,包括任务调度器的线程池大小、任务调度器的错误处理策略、任务调度器的任务执行器等。

下面是schedulingconfigurer中常用的方法及其作用:
configureTasks(TaskScheduler taskScheduler):用于配置任务调度器的线程池大小和错误处理策略。可以通过TaskScheduler对象设置线程池大小和错误处理策略。
setTaskScheduler(TaskScheduler taskScheduler):设置任务调度器。可以通过该方法设置任务调度器的具体实现类,如ThreadPoolTaskScheduler。
setTaskExecutor(Executor taskExecutor):设置任务执行器。可以通过该方法设置任务执行器的具体实现类,如ThreadPoolExecutor。
addTriggerTask(Runnable task, Trigger trigger):添加触发任务。可以通过该方法添加一个触发任务,指定任务执行的时间和周期。
addCronTask(Runnable task, String cronExpression):添加Cron任务。可以通过该方法添加一个Cron任务,指定任务执行的时间和周期。
addFixedDelayTask(Runnable task, long delay):添加固定延迟任务。可以通过该方法添加一个固定延迟任务,指定任务执行的延迟时间。
addFixedRateTask(Runnable task, long interval):添加固定间隔任务。可以通过该方法添加一个固定间隔任务,指定任务执行的间隔时间。
总之,schedulingconfigurer是一个非常重要的接口,它提供了许多方法,可以帮助我们配置任务调度器,实现定时任务的执行。在使用Spring框架进行开发时,我们可以通过实现该接口来自定义任务调度器的行为,以满足我们的具体需求。

简单实现
@Configuration
public class TestJob2 implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        //堆代码 duidaima.com
        //模拟多任务,实际可以从数据库或配置文件获取
        for(int i =0;i<3;i++){
            int finalI = i;
            //1.添加任务内容(Runnable)
            Runnable runnable = () -> {
                System.out.println("执行动态定时任务"+ finalI +":" + LocalDateTime.now());
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            };

            //2.任务触发器
            Trigger trigger = triggerContext -> {
                //获取定时触发器,可以从数据库或配置文件获取最新记录,更新触发器实现定时间隔的动态调整
                CronTrigger cronTrigger = new CronTrigger("*/2 * * * * ?");
                return cronTrigger.nextExecutionTime(triggerContext);
            };

            //3.注册任务
            scheduledTaskRegistrar.addTriggerTask(runnable, trigger);
        }
    }
}
执行结果
执行动态定时任务0:2023-03-20T09:47:38.010
执行动态定时任务1:2023-03-20T09:47:43.015
执行动态定时任务2:2023-03-20T09:47:48.019
执行动态定时任务0:2023-03-20T09:47:53.026
执行动态定时任务1:2023-03-20T09:47:58.040
执行动态定时任务2:2023-03-20T09:48:03.048
可以明显看出两个任务是串行执行,后面会介绍如何实现多任务并行执行

多任务如何并行
方式一:
@Configuration
public class ScheduleConfig {
    /**
     * 修复同一时间无法执行多个定时任务问题。@Scheduled默认是单线程的
     */
    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        //核心线程池数量,方法: 返回可用处理器的Java虚拟机的数量。
        taskScheduler.setPoolSize(Runtime.getRuntime().availableProcessors() * 2);
        return taskScheduler;
    }
}
方式二:
spring:
  application:
    name: test-task
  task:
    scheduling:
      pool:
        size: 8 #配置Scheduled定时任务为多线程
方式三:
@Configuration
public class ScheduleConfig1 implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        scheduledTaskRegistrar.setScheduler(
                new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors() * 2)
        );
    }
}
注意:
这里有的小伙伴说@EnableAsync @async @schedule一起使用,多任务是并行,但该方法会导致同一个任务,即使上一次执行还未完成,只要时间到就会再次执行该任务,如果任务执行顺序对结果没有影响可以用,如果单个任务执行有顺序之分,这种方式会导致结果的不准确。
用户评论