首页
学习
活动
专区
圈层
工具
发布

Spring Task定时任务:3行代码解放双手,告别重复劳动!

各位,是不是每天上线、拉数据、生成报表、清缓存、发通知、重试失败任务这些琐事已经搞得你心力交瘁?别急,今天咱聊个实用到哭的“老朋友”:Spring Task。

讲真,这玩意儿看起来不起眼,写起来也就三行配置 + 一个注解,但用好了,能把你从一堆机械活中彻底解放出来。什么“凌晨定时清理缓存”、“定时同步第三方数据”、“每晚跑批任务”……全都交给它就行了,稳定、高效、还不要你盯着。

很多刚入坑的小伙伴可能以为定时任务得上Quartz、Elastic-Job、xxl-job那种大杀器才靠谱。但我跟你讲,八九不离十的业务需求,Spring Task 三板斧就能搞定。

先来个最简单的:

@Configuration

@EnableScheduling

public class ScheduleConfig {

}

@Component

public class MyTask {

  @Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点执行

  public void clearCache() {

      System.out.println("清理缓存完成");

  }

}

没错,就这仨东西:一个注解@EnableScheduling,一个@Component Bean,加一个@Scheduled方法,你的定时任务就上线了。

想多定几个任务?加几个方法就行。 想控制执行频率?支持cron表达式、fixedRate、fixedDelay、initialDelay,怎么花式都可以。

比如每10秒执行一次:

@Scheduled(fixedRate = 10000)

public void autoReport() {

  System.out.println("定时上报数据");

}

注意,fixedRate是从任务开始算,fixedDelay是从任务结束算,这两个要是搞混了,任务调度就会打架,别怪我没提醒你。

再说点我踩过的坑,不然你们觉得定时任务就这点活,那就小瞧它了。

第一坑:多线程并发执行,结果混乱或任务重叠

默认Spring Task是单线程执行的,一个任务卡住了,后面的任务都得排队。这个设计对我们来说有好有坏,坏是不能并发,好是不用担心同一个任务被多个线程抢着跑。但有时候你确实需要并发,比如十个城市同步天气数据,那你得手动开线程池:

@Configuration

@EnableScheduling

public class ScheduleConfig implements SchedulingConfigurer {

  @Override

  public void configureTasks(ScheduledTaskRegistrar registrar) {

      registrar.setScheduler(Executors.newScheduledThreadPool(5));

  }

}

这时候,每个任务都能并发执行,但你得确保线程安全,别用个共享变量结果被改得乱七八糟,尤其是像静态变量、单例模式下的公共资源,那叫一个坑死人。

第二坑:任务执行太久,下一次调度时间到了还没跑完

想想你配置的是每10秒执行一次任务,如果某次任务因为网络抖动卡了15秒,那下一次调度就重叠了。你要不控制一下,可能直接两个线程一起跑同一段逻辑,结果数据库爆了、接口炸了、服务挂了。

解决方案也简单:搞个任务锁机制,比如用Redis的SETNX或者数据库加乐观锁,确保一个时间段内只允许一个任务执行。

第三坑:服务部署多个实例后任务重复执行

这个是最多人掉坑的场景之一。你本地测试好好的,一上线集群部署,结果你那“每天凌晨自动清缓存”任务,居然跑了三遍。为啥?因为你部署了三个实例,大家一起上岗……

解决方案其实也不复杂:要么通过分布式任务调度框架来统一控制(比如xxl-job、Quartz集群版),要么用Redis做分布式锁,拿到锁的那个实例执行任务,其他就乖乖等着。

我自己习惯是用Redisson做锁,写个注解 + AOP拦一下,很丝滑:

@Scheduled(cron = "0 0 * * * ?")

public void syncData() {

  RLock lock = redissonClient.getLock("syncDataLock");

  if (lock.tryLock()) {

      try {

          // 真正要执行的逻辑

      } finally {

          lock.unlock();

      }

  }

}

其实说到底,Spring Task真的很适合用在“轻量化定时任务”场景,尤其是那种日常重复任务、少量逻辑、无需复杂调度的。你搞个大框架纯属杀鸡用牛刀,不如这三行代码,清爽又好维护

当然了,要是你是那种“亿级别流量、高并发分布式任务调度”的场景,Spring Task就不够用了,建议你看看xxl-job、Quartz集群版、或者直接上K8s CronJob配合队列来异步执行,都比硬撸靠谱。

总结一下:

Spring Task适合中小项目、单体应用、定期执行的轻量任务;

用多线程配合线程池,可以避免任务卡死的问题;

注意任务锁和集群重复执行的坑,别把线上环境当本地;

轻量好维护,比那堆大而全的定时框架爽多了;

真要跑复杂调度的,记得上专业工具,不要强行用Spring Task上天。

说了这么多,也不想给Spring Task戴高帽子,就像它名字一样——Task,就是干活儿的,你别指望它变成“调度中心”,但你得知道,手里的小刀也是刀,关键时候照样能剁个猛的

你们公司都还在用什么跑定时任务?有没有那种“历史遗留”的怪异框架,一改任务得改XML配置还得重启?欢迎在评论区来个技术吐槽局,我已经准备好瓜子了

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OuMfR-OcqlbPCyGxNUWpZifg0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。
领券