各位被Thread.sleep()
和while(true)
折磨的Spring道友们!今天要解锁的是Spring生态自带的定时任务三件套 ------@Scheduled
、TaskScheduler
、@Async
定时组合技!无需引入外部依赖,轻松实现从简单定时到分布式调度的全场景覆盖!准备好抛弃Quartz的复杂配置了吗? ⏰
一、筑基篇:@Scheduled基础用法
1.1 开启定时任务(激活灵脉)
java
@SpringBootApplication
@EnableScheduling // 关键注解!放在启动类上
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
1.2 定时方法配置(三套仙术)
java
@Component
public class MyTask {
// 1. 固定速率(上次开始后间隔固定时间)
@Scheduled(fixedRate = 5000) // 每5秒执行
public void task1() {
System.out.println("固定速率任务:" + LocalDateTime.now());
}
// 2. 固定延迟(上次结束后间隔固定时间)
@Scheduled(fixedDelay = 3000) // 每次执行完等3秒
public void task2() throws InterruptedException {
Thread.sleep(1000); // 模拟耗时
System.out.println("固定延迟任务:" + LocalDateTime.now());
}
// 3. Cron表达式(天庭历法)
@Scheduled(cron = "0 0/5 9-18 * * MON-FRI") // 工作日9-18点每5分钟
public void task3() {
System.out.println("Cron任务:" + LocalDateTime.now());
}
}
二、金丹篇:高级配置技巧
2.1 动态修改定时规则(天机可变)
java
@RestController
public class TaskController {
@Autowired
private ScheduledTaskRegistrar taskRegistrar;
@PostMapping("/update-task")
public String updateTask(@RequestParam String newCron) {
// 取消原有任务
taskRegistrar.destroy();
// 添加新任务
taskRegistrar.addCronTask(
() -> System.out.println("动态任务执行: " + LocalDateTime.now()),
newCron
);
taskRegistrar.afterPropertiesSet();
return "任务已更新为: " + newCron;
}
}
2.2 异步定时任务(分身术)
java
@Configuration
@EnableAsync // 开启异步支持
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.initialize();
return executor;
}
}
@Component
public class AsyncTask {
@Async
@Scheduled(fixedRate = 3000)
public void asyncTask() {
System.out.println("异步任务线程:" + Thread.currentThread().getName());
}
}
三、元婴篇:分布式定时任务
3.1 基于Redis的分布式锁(防重复执行)
java
@Component
public class DistributedTask {
@Autowired
private RedissonClient redisson;
@Scheduled(cron = "0 0/5 * * * ?")
public void distributedJob() {
RLock lock = redisson.getLock("scheduled:lock");
try {
if (lock.tryLock(0, 30, TimeUnit.SECONDS)) {
System.out.println("获取锁成功,执行任务...");
// 业务代码...
}
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
3.2 结合ShedLock实现(推荐)
xml
<!-- 添加依赖 -->
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-spring</artifactId>
<version>4.42.0</version>
</dependency>
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-jdbc-template</artifactId>
<version>4.42.0</version>
</dependency>
java
@Configuration
@EnableSchedulerLock(defaultLockAtMostFor = "10m")
public class ShedLockConfig {
@Bean
public LockProvider lockProvider(DataSource dataSource) {
return new JdbcTemplateLockProvider(dataSource);
}
}
@Service
public class ShedLockTask {
@Scheduled(cron = "0 0/5 * * * ?")
@SchedulerLock(name = "reportTask", lockAtLeastFor = "5m")
public void scheduledTask() {
// 保证同一时间只有一个实例执行
}
}
四、化神篇:监控与管理
4.1 暴露执行端点(Spring Boot Actuator)
yaml
# application.yml
management:
endpoints:
web:
exposure:
include: scheduledtasks
endpoint:
scheduledtasks:
enabled: true
访问/actuator/scheduledtasks
查看所有定时任务:
json
{
"cron": [
{
"runnable": {
"target": "com.example.MyTask.task3"
},
"expression": "0 0/5 9-18 * * MON-FRI"
}
],
"fixedDelay": [],
"fixedRate": []
}
4.2 自定义任务监控(天眼通)
java
@Bean
public ScheduledTaskPostProcessor taskPostProcessor() {
return new ScheduledTaskPostProcessor() {
@Override
public void postProcessAfterInitialization(Object bean, String beanName) {
// 监控所有定时任务初始化
System.out.println("已加载定时任务: " + beanName);
}
};
}
五、大乘篇:最佳实践
5.1 任务执行日志记录
java
@Aspect
@Component
public class TaskLogAspect {
@Around("@annotation(scheduled)")
public Object logTask(ProceedingJoinPoint pjp, Scheduled scheduled) throws Throwable {
String taskName = pjp.getSignature().toShortString();
long start = System.currentTimeMillis();
try {
System.out.println("任务开始: " + taskName);
return pjp.proceed();
} finally {
System.out.printf("任务结束: %s, 耗时: %dms%n",
taskName, System.currentTimeMillis() - start);
}
}
}
5.2 异常处理策略
java
@Scheduled(fixedRate = 5000)
public void errorProneTask() {
try {
// 业务代码...
} catch (Exception e) {
// 1. 记录异常到数据库
// 2. 发送报警邮件
// 3. 根据策略决定是否重试
System.err.println("任务执行失败: " + e.getMessage());
}
}
渡劫指南:常见问题
问题 | 解决方案 |
---|---|
任务不执行 | 检查@EnableScheduling 是否启用 |
Cron表达式无效 | 使用在线工具校验表达式 |
多任务串行执行 | 配置TaskScheduler 线程池 |
分布式环境重复执行 | 集成ShedLock或Redis分布式锁 |
时区问题 | 设置spring.task.scheduling.pool.size |