Spring之定时任务和异步任务

Spring之定时任务和异步任务

一、定时任务

Spring框架下使用@Scheduled、@EnableScheduling两个注解可以快速开发定时器。

用法

在需要定时执行的方法上使用@Scheduled

在所属类上使用@EnableScheduling


定时规则

@Scheduled可以指定配置定时器的执行规则,参数如下。

  • cron

参数接收一个cron表达式,cron表达式是一个以空格为间隔符来区分不同域的字符串,总共有6个或7个域。cron表达式从左到右每个域分别标识的[秒] [分] [小时] [日] [月] [周] [年],其中[年]不是必选的域可以省略。

序号说明必填可选值允许的通配符
10-59, - * /
20-59, - * /
30-23, - * /
41-31, - * ? / L W
51-12 / JAN-DEC, - * /
61-7 / SUN-SAT, - * ? / L #
71970-2099, - * /

通配符说明:

*表示所有值。 例如:在时的字段上设置 *,表示每一个小时都会触发
?表示不指定值,即当前使用的场景为不需要关心这个字段设置的值。例如:要在每月的10号触发一个操作,但不关心是周几,所以需要周位置的那个字段设置为“?” 具体设置为 0 0 0 10 * ?

-表示区间。例如:在小时上设置 “10-12”,表示 10,11,12点都会触发
, 表示指定多个值,例如在周字段上设置 “MON,WED,FRI” 表示周一,周三和周五触发
/用于递增触发。如在秒上面设置“5/15” 表示从5秒开始,每增15秒触发(5,20,35,50)。在日字段上设置‘1/3’所示每月1号开始,每隔三天触发一次

示例

@Scheduled(cron = */5 * * * * ?)		// 每隔5秒执行一次
@Scheduled(cron = 0 */1 * * * ?)		// 每隔1分钟执行一次
@Scheduled(cron = 0 0 0 * * ?)			// 每天0点执行一次
@Scheduled(cron = 0 0 1 1 * ?)			// 每月1号凌晨1点执行一次
@Scheduled(cron = 0 0 0,12,18 * * ?)	// 每天的0点、12点、18点各执行一次
  • fixedDelay

上一次执行完成后延迟多久执行下一次,单位ms

@Scheduled(fixedDelay = 60000) // 上一次执行完成后延迟1min再执行
  • fixedRate

固定延迟多久执行下一次任务,不依赖于上一次任务执行成功的时间,单位ms

@Scheduled(fixedRate = 60000) // 上一次执行后延迟1min执行
  • initialDelay

启动后延迟多久后执行第一次,可根据场景搭配fixedRate或fixedDelay实现定时调度

@Scheduled(initialDelay = 5000,fixedRate= 60000) //启动后延迟5s执行,之后每次执行时间间隔1min

示例

@EnableScheduling
public class ScheduleTaskFunc {
 
    // 每天0点定时清理
    @Scheduled(cron = "0 0 0 * * ?")
    public void scheduleTaskMethod() {
        // method body
    }
}

二、异步任务

使用@EnableAsync和@Async来实现。

用法

在需要定时执行的方法上使用@Async,若在类上使用则表示该类所有方法都是异步方法

在所属类上使用@EnableAsync

要求:

  1. @Async注解的方法必须是public的方法,不能是private的方法

  2. 使用此注解的方法的类对象,必须是spring管理下的bean对象 (如被@Service、@Component等修饰的Bean对象)


默认线程池和自定义线程池

  • 默认线程池:使用@Async()注解时,若不指定线程池名称,则使用Spring默认的线程池,为SimpleAsyncTaskExecutor。该线程池默认8个核心线程,可通过修改application.yaml配置文件进行修改。
  • 自定义线程池:@Configuration + @Bean(name = “线程池名称”) + @Async(“线程池名”)
    • 定义一个异步类和执行器(可在此处指定核心线程数,最大线程数,队列大小等规格)
    • 使用@Async(“线程池名”)注解来指定使用这个自定义线程池

三、配合使用示例

(1)编写自定义线程池

@Configuration
@EnableAsync
public class AsyncTaskPool {
 
    @Bean(name = "asyncTaskExecutor")
    public Executor asyncTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(4); // 核心线程数
        executor.setMaxPoolSize(8); // 最大线程数
        executor.setQueueCapacity(100); // 队列大小
        executor.setKeepAliveSeconds(60); // 线程空闲时的存活时间
        executor.setThreadNamePrefix("MyThreadPoolTaskExecutor-"); // 线程名前缀
        executor.setRejectedExecutionHandler((ThreadPoolExecutor.AbortPolicy) -> {
            // 拒绝策略,例如记录日志或者抛出异常
        });
        executor.initialize(); // 初始化线程池
        return executor;
    }
}

(2)编写异步定时任务

@Component
@EnableAsync
@EnableScheduling
public class asyncScheduledFunc {

    @Async("asyncTaskExecutor")
    @Scheduled(cron = "0 0 0 * * ?")
    @SchedulerLock(name = "oneSchedulerLock", lockAtMostFor = "PT600S", lockAtLeastFor = "PT60S")
    public void asyncScheduledTask() {
        // method body
    }
}

补充:@SchedulerLock

@SchedulerLock为分布式锁,当服务为多节点部署且使用了@Scheduled编写定时任务,那么在同一时刻所有节点会同时执行定时任务,但我们可能希望只需要一个节点执行,这时就需要使用分布式锁控制执行过程。

参数:

  • name:任务唯一标识。同一个name的任务,同一个时刻,多个线程只会有一个线程获取到锁。其他没有获取到锁的线程会跳过,不会阻塞等待。
  • lockAtLeastFor:持有锁的最短时间。这个主要是防止不同节点时间存在误差。
  • lockAtMostFor:持有锁的最长时间。主要是为了防止死锁。当一个任务执行完成时会释放锁,当一个任务执行超过lockAtMostFor时间时,也会释放锁。这个时间要大于业务执行的时间,不然一个任务可能会被执行多次。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值