springboot实现定时任务-Scheduling

2023年5月30日12:08:45

1.主要总结@Scheduled()注解的三个属性:cron,fixedRate,fixedDelay

cron为cron表达式,用来表示该任务在日期时间维度执行频率,详细可参考cron表达式的文章

fixedRate:代表该任务的执行频率,单位毫秒,无论任务执行耗时多久,总是以该频率执行任务

fixedDelay:代表该任务的执行频率,单位毫秒,在上一次任务执行完后等待x毫秒后执行下次任务

2.开启异步@EnableAsync后使用上述三种属性产生的效果

前提:springboot通过scheduling实现的定时任务使用的线程有自己的线程池,默认大小为1,此时无法满足多个定时任务的情形,因此通常需要手动设置线程池的大小,以10为例。

有以下三种情形

1.使用fixedRate属性不加@Async注解、使用fixedRate属性加@Async注解

2.使用cron不加@Async注解、使用cron属性加@Async注解

3.使用fixedDelay不加@Async注解

@Component
@EnableAsync
@Slf4j
public class ScheduledTasks {

    @Scheduled(fixedDelay = 3000)
//    @Scheduled(fixedRate = 3000)
//    @Scheduled(cron = "0/3 * * * * ?")
//    @Async(value = "myAsync")
    void contextLoads() throws InterruptedException {
        log.info("scheduled1 wait start");
        Thread.sleep(4000);
        log.info("scheduled1 wait end");
        log.info("scheduled task run....      " + Thread.currentThread().getName());
    }

//    @Scheduled(cron = "0/3 * * * * ?")
    @Scheduled(fixedDelay = 3000)
//    @Scheduled(fixedRate = 3000)
//    @Async(value = "myAsync")
    void scheduled2() throws InterruptedException {
        log.info("scheduled2 wait start");
        Thread.sleep(4000);
        log.info("scheduled2 wait end");
        log.info("scheduled task2 run....      " + Thread.currentThread().getName());
    }
}

3.结论:

1.使用fixedRate加@Async注解和使用cron加@Async注解效果相同

会实现无论任务执行耗时多久,下次任务始终会以规定的频率执行;

上述例子中若为fixedRate,则下次任务开始执行始终是在上次任务开始后的三秒;

若为cron,则下次任务开始执行始终为上次任务开始执行后的三秒,也就是上次任务开始时间为10:23:02,则下次任务开始时间为10:23:05,此种情况不受任务执行耗时影响。

2.使用fixedRate不加@Async注解

此时若每次任务执行耗时比fixedRate的值小,则会在上次任务开始后的fixedRate毫秒后开始执行下次任务;

若每次执行任务耗时比fixedRate的值大,则会在上次任务执行后立刻执行下次任务,因为此时已经超过规定的执行频率的时间,因此需要立刻执行下次任务。

3.使用cron不加@Async注解

此时若每次任务执行耗时比cron表达式所表示的上次任务开始时间与下次任务开始时间的间隔小,则会以cron表达式表示的频率执行任务;

若每次任务执行耗时比cron表达式所表示的上次任务开始时间与下次任务开始时间的间隔大,则下次任务会在上次任务执行完成后再到达cron表达式所表示的任务开始执行的时刻开始执行任务,例如上述例子中上次任务开始时间为10:23:02,任务执行耗时4s,而以cron表达式表示的频率下次的执行时间为10:23:05,但是此时上次任务并没有执行完成,上次任务执行完成的时间为10:23:06,因此下次任务执行需要等到cron表达式表示的下一次任务执行的时刻也就是10:23:08.

4.使用fixedDelay不加@Async注解

使用fixedDelay属性时不能加@Async注解,否则会报错。

此时无论任务执行耗时是多少,下次任务开始执行的时间总是上次任务执行完成后的fixedDelay毫秒。

5.当有多个定时任务时,在保证定时任务线程池数以及异步线程池数足够的情况下,多个定时任务同样遵循上述结论。

6.上述结论中1和4是比较符合开发需要的两种场景。

4.修改定时任务线程池以及自定义异步任务线程池

1.修改定时任务默认的线程池大小

方式一:修改配置文件application.yml 

spring.task.scheduling.pool.size = 10

方式二:配置类

@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(50));
    }
}

2.自定义定时任务的线程池

@Configuration
public class ScheduleConfig {
 
    /**
     * 此处方法名为Bean的名字,方法名无需固定
     * 因为是按TaskScheduler接口自动注入
     */
    @Bean
    public TaskScheduler taskScheduler(){
        // Spring提供的定时任务线程池类
        ThreadPoolTaskScheduler taskScheduler=new ThreadPoolTaskScheduler();
        //设定最大可用的线程数目
        taskScheduler.setPoolSize(10);
        return taskScheduler;
    }
}

3.自定义异步任务线程池

@Component
public class AsyncScheduledTaskConfig {

    @Bean
    public Executor myAsync() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //最大线程数
        executor.setMaxPoolSize(100);
        //核心线程数
        executor.setCorePoolSize(10);
        //任务队列的大小
        executor.setQueueCapacity(10);
        //线程前缀名
        executor.setThreadNamePrefix("task-thread");
        //线程存活时间
        executor.setKeepAliveSeconds(30);

        /**
         * 拒绝处理策略
         * CallerRunsPolicy():交由调用方线程运行,比如 main 线程。
         * AbortPolicy():直接抛出异常。
         * DiscardPolicy():直接丢弃。
         * DiscardOldestPolicy():丢弃队列中最老的任务。
         */
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        //线程初始化
        executor.initialize();
        return executor;
    }
}

使用时只需在@Async注解中使用value属性指定线程线程池名称,也就是bean的名称,上述默认为方法名

  • 作者:冯坤鹏11
  • 原文链接:https://blog.csdn.net/u012785397/article/details/127378583
    更新时间:2023年5月30日12:08:45 ,共 3212 字。