定时任务@Scheduled 和 异步@Async

时间:2025-05-06 12:41:18

文章目录

    • 概述
    • cron表达式
    • 什么是异步
    • @Scheduled

概述

Spring 3.0 版本之后自带定时任务,提供了@EnableScheduling注解和@Scheduled注解来实现定时任务功能。

使用SpringBoot创建定时任务非常简单,目前主要有以下三种创建方式:

1、基于注解(@Scheduled
2、基于接口(SchedulingConfigurer) 前者相信大家都很熟悉,但是实际使用中我们往往想从数据库中读取指定时间来动态执行定时任务,这时候基于接口的定时任务就派上用场了。
3、基于注解设定多线程定时任务

cron表达式

Cron 表达式是一种用于配置定时任务的时间格式。它起源于 Unix 系统的 cron 定时任务调度工具,现在被广泛应用于各种编程语言和平台中,用于定义定时执行任务的时间规则。Cron 表达式通常由六个或七个字段组成,每个字段代表一个时间单位,从左到右分别是:

    1. 秒(0-59)
    1. 分钟(0-59)
    1. 小时(0-23)
    1. 日期(1-31)
    1. 月份(1-12 或 JAN-DEC)
    1. 星期(0-7 或 SUN-SAT,其中 0 和 7 都表示星期日)
    1. 年份(可选,1970-2099)

Cron 表达式中使用了一些特殊的字符来表示不同的时间规则:

  • *:表示所有可能的值。例如,* 在分钟字段中表示每一分钟。
  • ,:表示列表值。例如,1,15 表示第 1 分钟和第 15 分钟。
  • -:表示一个范围。例如,1-5 表示从第 1 分钟到第 5 分钟。
  • /:表示增量。例如,0/15 表示从第 0 分钟开始,每隔 15 分钟。
  • ?:表示不指定值,通常用于日期和星期字段。当其中一个字段被指定了具体的值时,另一个字段应使用 ?。
  • L:表示最后一天或最后一个。例如,L 在日期字段中表示该月的最后一天,L 在星期字段中表示该月的最后一个星期几。
  • W:表示最接近指定日期的工作日。例如,15W 表示最接近 15 号的工作日。
  • #:表示特定的星期几。例如,6#3 表示该月的第三个星期五。

cron表达式在线生成

什么是异步

异步: 异步调用则是只是发送了调用的指令,调用者无需等待被调用的方法完全执行完毕;而是继续执行下面的流程。例如, 在某个调用中,需要顺序调用 A, B, C三个过程方法;如他们都是同步调用,则需要将他们都顺序执行完毕之后,方算作过程执行完毕; 如B为一个异步的调用方法,则在执行完A之后,调用B,并不等待B完成,而是执行开始调用C,待C执行完毕之后,就意味着这个过程执行完毕了。在Java中,一般在处理类似的场景之时,都是基于创建独立的线程去完成相应的异步调用逻辑,通过主线程和不同的业务子线程之间的执行流程,从而在启动独立的线程之后,主线程继续执行而不会产生停滞等待的情况。

@Scheduled

TaskScheduler: 任务调度者
TaskExecutor : 任务执行者

@EnableScheduling 开启定时任务功能,方法所在类需要加@Component
@Scheduled(Cron= "") 指定执行的时间

	@Scheduled(cron = "0/1 * * * * ? ")
    @Override
    public Result ScanUserState() throws InterruptedException {
        Thread.sleep(5000);
        System.out.println("执行了");
        return new Result();
    }

定时任务,只有一个线程去执行任务,任务间隔是1000毫秒,执行任务需要5000毫秒,那么任务的执行周期是1000+5000=6000毫秒。就算线程池中有多个线程,也会等那个任务执行完再选取一个线程再次执行任务。

如果同时开启了@EnableAsync和使用了@Async,如果执行时间大于Cron表达式的时间,则会选取线程池中其他的线程立刻去执行任务,如果没有剩余线程,则会等待有线程空闲出来。

@Configuration
public class ScheduleConfig implements SchedulingConfigurer,AsyncConfigurer {

    //并行任务
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar){
        TaskScheduler taskScheduler = taskScheduler();
        taskRegistrar.setTaskScheduler(taskScheduler);
    }


    //并行任务使用策略:多线程处理(配置线程数等)
    @Bean("task1")
    public ThreadPoolTaskScheduler taskScheduler(){
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(20);
        scheduler.setThreadNamePrefix("task-");  //设置线程名开头
        scheduler.setAwaitTerminationSeconds(60);
        scheduler.setWaitForTasksToCompleteOnShutdown(true);
        return scheduler;
    }

    //异步任务
    public Executor getAsyncExecutor(){
        Executor executor = taskScheduler();
        return executor;
    }

    //异步任务 异常处理
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler(){
        return new SimpleAsyncUncaughtExceptionHandler();
    }
}

  	@Async("task1")
    @Scheduled(cron = "0/1 * * * * ? ")
    @Override
    public Result ScanUserState() throws InterruptedException {
        Thread.sleep(5000);
        System.out.println("执行了");
        return new Result();
        // return ();
    }

努力的人从不孤独,因为终有一天,我们都会在耀眼的光芒处相逢。如果在阅读过程中发现任何错误或有任何疑问,欢迎在评论区留言交流。技术之路,共同进步。如果觉得这篇文章有用,不妨点赞、分享给更多需要的人。感谢您的阅读!