1、定时任务
1)cron表达式
语法:
秒 分 时 日 月 周(周几) 年(Spring不支持年,支持前6位)
特殊字符
① , :枚举
7,9,23 * * * * ? 任意时刻的7,9,23秒启动该任务
② - :范围
7-20 * * * * ? 任意时刻的7-20秒,每秒启动一次
③ * :任意 指定位置的任意时刻
④ / :步长
7/5 * * * * ? 任意时刻的第七秒启动,每5秒执行一次
*/5 * * * * ? 任意秒启动,每5秒执行一次
⑤ ?:
(出现在日和周几的位置):为了防止日和周冲突,在周和日上如果要写通配符?
⑥ L:(出现在日和周的位置)
last:最后一个 (cron="* * * ? * 3L"):每月的最后一个周二
⑦ W:
Work Day:工作日 (cron="* * * W * ?"):每个月的工作日触发
(cron="* * * LW * ?"):每个月的最后一个工作日触发
⑧ #:第几个
(cron="* * * ? * 5#2"):每个月的第 2 个周 4
2)SpringBoot整合corn表达式做定时任务
/* * 定时任务 * 1、@EnableScheduling 开启定时任务功能 * 2、@Scheduled 开启定时任务 * 3、自动配置类: TaskSchedulingAutoConfiguration * * 异步任务 * 1、@EnableAsync: 开启异步任务 * 2、在方法上标注 @Async * 3、自动配置类: TaskExecutionAutoConfiguration * 属性默认绑定: TaskExecutionProperties */ @Slf4j @EnableAsync @EnableScheduling @Component public class HelloSchedule { /* * 1、cron表达式不支持第7位的年 * 2、周: 1-7 代表周一到周日 * 3、定时任务不应该阻塞。默认是阻塞的 * 1)让业务以异步的方式自己提交到线程池 * CompletableFuture.runAsync(()->{ * xxxxService.hello(); * }, executor); 2)支持定时任务线程池 设置 TaskSchedulingProperties(定时任务配置绑定类) spring.task.scheduling.pool.size: xxx => 有些版本不一定好使 3)直接让定时任务异步执行 @EnableAsync + @Async 使用异步 + 定时任务来完成定时任务不阻塞的功能 * */ @Scheduled(cron = "* * * * * ?") public void hello() throws InterruptedException { log.info("hello。。。"); Thread.sleep(3000); } }
3)、定时任务-分布式系统下的问题
当同个服务启动多个时:
①、需要使用分布式锁,来保证只有一个服务来执行定时任务。
②、同时需要保证定时任务执行的幂等性
@Service public class SeckillSkuScheduled { @Autowired SeckillService seckillService; @Autowired RedissonClient redissonClient; private final String upload_lock = "seckill:upload:lock"; //todo 幂等性问题 -> 同一件秒杀商品不应该上架两次 @Scheduled(cron = "0 * * * * ?") public void uploadSeckillSkuLatest3Days(){ //重复上架无需处理 System.out.println("秒杀商品上架..."); //加上分布式锁,保证只有一个定时任务执行 RLock lock = redissonClient.getLock(upload_lock); lock.lock(10, TimeUnit.SECONDS); //十秒中没有执行完,自动释放锁,不会造成死锁 try { seckillService.uploadSeckillSkuLatest3Days(); } finally { lock.unlock(); } } }