Spring 定时任务与异步编程:@Scheduled 与 @Async 实战

Spring 定时任务与异步编程:@Scheduled 与 @Async 实战

一、为什么需要定时与异步?

在后端系统中,常常会遇到以下需求:

  • 每天定时清理日志或数据
  • 定期发送邮件或短信提醒
  • 异步执行耗时任务,避免阻塞主线程

Spring 提供了非常轻量的方式处理这些场景:@Scheduled@Async


二、@Scheduled:定时任务注解

Spring Boot 原生支持定时任务,只需:

1. 开启定时任务支持

@SpringBootApplication
@EnableScheduling
public class MyApplication {}

2. 定义定时任务方法

@Component
public class TaskService {

    @Scheduled(fixedRate = 5000)
    public void reportCurrentTime() {
        System.out.println("每5秒执行一次:" + LocalDateTime.now());
    }

    @Scheduled(cron = "0 0 3 * * ?")
    public void cleanUpData() {
        System.out.println("每天凌晨3点执行清理任务");
    }
}

三、@Scheduled 常用参数

属性说明
fixedRate上一次开始执行后多长时间再次执行
fixedDelay上一次执行完成后多长时间执行
initialDelay初次延迟
cron使用 cron 表达式进行时间控制

示例:

// 每小时整点执行
@Scheduled(cron = "0 0 * * * ?")

// 每天早上9点执行
@Scheduled(cron = "0 0 9 * * ?")

// 每10秒执行
@Scheduled(fixedRate = 10000)

四、如何避免任务重复执行?

Spring 默认定时任务是串行执行的。如果任务耗时较长,可能导致下次调度错过。

解决方式:

@EnableAsync
@EnableScheduling
@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar registrar) {
        registrar.setScheduler(Executors.newScheduledThreadPool(5));
    }
}

五、@Async:异步任务执行

1. 开启异步支持

@SpringBootApplication
@EnableAsync
public class MyApplication {}

2. 异步执行方法

@Component
public class EmailService {

    @Async
    public void sendEmail(String to) {
        // 模拟耗时操作
        Thread.sleep(3000);
        System.out.println("发送邮件到:" + to);
    }
}

3. 调用异步方法

@Autowired
private EmailService emailService;

@GetMapping("/notify")
public String notifyUser() {
    emailService.sendEmail("[email protected]");
    return "邮件发送中(异步)";
}

六、自定义线程池(推荐)

默认线程池可配置,以避免使用默认的 SimpleAsyncTaskExecutor。

@Configuration
public class AsyncConfig {
    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(4);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("async-task-");
        executor.initialize();
        return executor;
    }
}

七、定时 + 异步的组合用法

某些定时任务本身比较耗时,可直接结合:

@Scheduled(fixedRate = 10000)
@Async
public void syncData() {
    System.out.println("执行异步数据同步任务:" + LocalDateTime.now());
}

八、使用 Spring Boot 配置控制定时任务

你可以在 application.yml 中控制是否启用某些定时任务:

my:
  task:
    enabled: true

结合条件注解:

@Scheduled(cron = "0 0 * * * ?")
@ConditionalOnProperty(name = "my.task.enabled", havingValue = "true")
public void doTask() {
    ...
}

九、异常处理与监控建议

  • 使用 try-catch 保证定时任务不会异常终止
  • 记录任务执行日志
  • 接入 Prometheus / Micrometer 监控任务执行耗时

十、总结

组件用途
@Scheduled定时任务调度
@Async异步任务执行
SchedulingConfigurer自定义线程池管理任务
AsyncConfigurer自定义异步执行线程池

通过这两个注解,你可以轻松构建高并发、低耦合的任务执行系统。


下一篇将聚焦在 Java Web 实战中常用的缓存技术:

《Java 缓存实战:使用 Redis + Spring Cache 优化性能》

是否继续生成第 8 篇?

Comments

No comments yet. Why don’t you start the discussion?

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注