--- title: "15-定时任务" created: 2025-12-02 tags: - 项目 aliases: - 定时任务 --- # 定时任务 @Scheduled @Scheduled(fixedDelay = 5000) 每隔5s执行一次 定时任务在现代项目中非常常见,尤其是在需要定期执行一些后台任务的场景下。`@Scheduled` 注解是 Spring Framework 中提供的一种非常方便的定时任务调度工具,它可以帮助我们自动化地完成很多重复性、时间驱动的操作。以下是一些典型场景和常用的扩展用法。 ### 常见场景 1. **数据同步** - **场景描述**:例如,定期将本地数据库的数据同步到外部系统,或者从外部系统拉取数据进行处理。这些操作通常不需要用户干预,且执行频率相对固定。 - **示例**:每 30 分钟从第三方 API 拉取数据并更新本地数据库。 - **使用的定时任务配置**: ``` @Scheduled(cron = "0 0/30 * * * ?") // 每30分钟执行一次 public void syncDataFromApi() { // 拉取数据并更新数据库 } ``` 2. **日志清理** - **场景描述**:定期清理过期的日志文件或者旧的记录。为了防止日志文件无限增长,通常会设定定期清理机制。 - **示例**:每周清理 30 天前的日志文件。 - **使用的定时任务配置**: ``` @Scheduled(cron = "0 0 0 * * 1") // 每周一凌晨 12 点清理过期日志 public void cleanOldLogs() { // 删除30天前的日志文件 } ``` 3. **缓存清理与刷新** - **场景描述**:为了提高系统性能,可以使用缓存来存储频繁访问的数据,但缓存的内容需要定期更新或清理。 - **示例**:每隔 1 小时清理缓存并刷新。 - **使用的定时任务配置**: ``` @Scheduled(fixedRate = 3600000) // 每1小时清理一次缓存 public void refreshCache() { // 刷新或清理缓存 } ``` 4. **发送定时邮件通知** - **场景描述**:例如,系统需要定期发送邮件通知用户,如每月的账单通知、促销活动提醒等。 - **示例**:每月的 1 号凌晨 1 点发送账单邮件。 - **使用的定时任务配置**: ``` @Scheduled(cron = "0 0 1 1 * ?") // 每月的1号凌晨1点 public void sendMonthlyBilling() { // 发送账单邮件 } ``` 5. **任务调度和队列处理** - **场景描述**:在分布式系统中,可能会使用定时任务来定期检查任务队列、处理未完成的任务或重试失败的任务。 - **示例**:每 10 分钟检查一次任务队列,处理待执行的任务。 - **使用的定时任务配置**: ``` @Scheduled(fixedDelay = 600000) // 每10分钟执行一次 public void processTaskQueue() { // 检查并处理队列中的任务 } ``` 6. **健康检查与监控** - **场景描述**:定期检查系统的健康状态,如数据库连接、服务接口可用性等,或者定期生成系统监控报告。 - **示例**:每 5 分钟执行一次健康检查,检测服务是否正常。 - **使用的定时任务配置**: ``` @Scheduled(fixedRate = 300000) // 每5分钟执行一次 public void healthCheck() { // 执行健康检查,检查数据库连接、外部API等服务状态 } ``` --- ### 扩展用法 #### 1. **使用** `initialDelay` **来延迟任务的第一次执行** 有时候你可能不希望任务立即开始执行,而是希望延迟一段时间后才开始,特别是在系统启动时。`initialDelay` 参数可以让你设定首次执行任务之前的延迟。 ```java @Scheduled(fixedRate = 10000, initialDelay = 5000) // 延迟5秒后开始执行,每10秒执行一次 public void performTaskWithInitialDelay() { System.out.println("延迟任务执行时间: " + System.currentTimeMillis()); } ``` #### 2. **并发问题** Spring 的 `@Scheduled` 默认是单线程执行的,这意味着即使有多个定时任务,如果一个任务执行时间较长,其他任务会等待。如果有多个任务可能同时执行,需要确保线程安全,或考虑使用 `@Async` 配合定时任务来处理并发。 ```java @Scheduled(fixedRate = 5000) @Async // 使用@Async标记为异步执行,避免阻塞 public void performAsyncTask() { System.out.println("异步任务执行时间: " + System.currentTimeMillis()); } ``` #### 3. **使用** `cron` **表达式进行复杂的调度** `cron` 表达式提供了更灵活的调度方式,允许你设定更复杂的时间规则。例如,可以设置在每天的特定时间执行,或者每月的某些日期执行等。 - **例子 1**:每天凌晨 1 点执行任务 ```java @Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1点执行 public void dailyTask() { System.out.println("每日任务执行时间: " + System.currentTimeMillis()); } ``` - **例子 2**:每周一和周三的下午 2 点执行任务 ```java @Scheduled(cron = "0 0 14 ? * MON,WED") // 每周一和周三下午2点执行 public void weeklyTask() { System.out.println("每周任务执行时间: " + System.currentTimeMillis()); } ``` #### 4. **处理任务失败和重试** 对于定时任务中的任务失败处理,可以通过集成 Spring 的 `@Retryable` 注解来实现失败后重试逻辑,或者使用某些框架(如 Quartz)来实现任务失败后的重试策略。 #### 5. **动态控制定时任务的开启与关闭** 有时候可能需要动态控制某些定时任务的开启与关闭,可以结合 `@Scheduled` 和 `@EnableScheduling`,通过在代码中动态判断是否开启任务。 ```java import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class DynamicTaskScheduler { private boolean isTaskEnabled = true; // 控制任务是否执行 @Scheduled(fixedRate = 10000) public void conditionalTask() { if (isTaskEnabled) { System.out.println("任务执行时间: " + System.currentTimeMillis()); } } public void toggleTask(boolean enable) { this.isTaskEnabled = enable; } } ``` 通过这种方式,你可以在代码中动态开启或停止某些定时任务。 --- ### 总结 定时任务是很多项目中的常见需求,特别是用于后台自动化处理的场景。通过 `@Scheduled` 注解,我们可以非常方便地创建定时任务,常见用法包括定时同步数据、清理日志、刷新缓存等。在使用时,根据具体场景选择合适的调度策略,如 `fixedRate`、`fixedDelay`、`cron` 等,并确保处理好并发和任务失败的场景。 根据项目的不同需求,定时任务的灵活性和可扩展性可以进一步增强,保证系统的高效性和可维护性。 --- **项目分区导航**:⬅️ [[14-组队功能|14-组队功能]] | 15-定时任务 | ➡️ [[16-缓存|16-缓存]]