Spring定时任务修仙指南:从@Scheduled到分布式调度的终极奥义

各位被Thread.sleep()while(true)折磨的Spring道友们!今天要解锁的是Spring生态自带的定时任务三件套 ------@ScheduledTaskScheduler@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

相关推荐
斑驳的岁月9 分钟前
MacOs java环境配置+maven环境配置踩坑实录
java·macos·maven
瀚海澜生12 分钟前
链表系列进阶攻略(三):拆解 LRU 与分割链表,突破链表解题瓶颈
后端·算法
严文文-Chris13 分钟前
方法区、堆、虚拟机栈、寄存器分别存储哪些内容?为什么存储这些内容?
java·开发语言
qq_4850152120 分钟前
Java网络编程干货
java·网络·php
bobz96520 分钟前
supervisord 的使用
后端
大道无形我有型21 分钟前
📖 Spring 事务机制超详细讲解(哥们专属)
后端
Re27522 分钟前
springboot源码分析--自动配置流程
spring boot·后端
Piper蛋窝26 分钟前
Go 1.2 相比 Go1.1 有哪些值得注意的改动?
后端·go
努力的搬砖人.29 分钟前
java爬虫案例
java·经验分享·后端
Miraitowa_cheems38 分钟前
JAVA SE 自我总结
java·开发语言·javase