一、闹钟式编程:为什么需要定时任务?
每天早上7点自动执行的晨跑提醒,双十一0点的秒杀活动触发,财务报表的月末自动生成...这些场景都在使用定时任务。Spring Boot为我们提供了@Scheduled注解和Quartz两种武器,就像单兵作战装备和集团军作战系统,应对不同的战场需求。
二、快速上手:@Scheduled原住民方案
2.1 启动定时宇宙的开关
要让@Scheduled生效,先启动定时任务宇宙:
@SpringBootApplication
@EnableScheduling // 宇宙大爆炸开关
public class TaskApplication {
public static void main(String[] args) {
SpringApplication.run(TaskApplication.class, args);
}
}
2.2 三种基础时间配置法
@Component
public class BasicTask {
// 每次执行结束后间隔3秒(看门狗模式)
@Scheduled(fixedDelay = 3000)
public void feedDog() {
System.out.println("🐶 喂狗完成:" + new Date());
}
// 无视执行时间,每5秒准时出发(地铁运行模式)
@Scheduled(fixedRate = 5000)
public void subwayRun() {
System.out.println("🚇 地铁发车:" + new Date());
}
// 凌晨执行数据归档(天文台模式)
@Scheduled(cron = "0 0 3 * * ?")
public void dataArchive() {
System.out.println("📦 数据归档:" + new Date());
}
}
2.3 高精度时间配置示例
# application.properties
# 动态配置间隔时间(单位:毫秒)
task.interval=7000
@Scheduled(fixedRateString = "${task.interval}")
public void dynamicTask() {
System.out.println("🎯 动态间隔任务:" + new Date());
}
2.4 并发控制实战
默认情况下,定时任务都是单线程执行:
@Configuration
public class AsyncConfig {
@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5); // 核心线程数
executor.setMaxPoolSize(10); // 最大应急线程数
executor.setQueueCapacity(20); // 等候区座位数
executor.setThreadNamePrefix("task-"); // 工牌前缀
return executor;
}
}
@Component
@EnableAsync
public class ConcurrentTask {
@Async("taskExecutor") // 启用异步执行
@Scheduled(fixedRate = 2000)
public void multiThreadTask() {
System.out.println(Thread.currentThread().getName()
+ " 正在工作:" + new Date());
}
}
三、专业级方案:Quartz集团军作战
3.1 Quartz的三大核心装备
- Job:具体作战任务(如发送邮件)
- Trigger:作战时间表(如每周一8点)
- Scheduler:作战指挥部
3.2 Spring Boot整合Quartz
// 系统管理员的记事本
public class EmailJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) {
System.out.println("📧 发送日报邮件:" + new Date());
}
}
@Configuration
public class QuartzConfig {
@Bean
public JobDetail emailJobDetail() {
return JobBuilder.newJob(EmailJob.class)
.withIdentity("dailyEmail")
.storeDurably() // 持久化任务
.build();
}
@Bean
public Trigger emailTrigger() {
CronScheduleBuilder schedule = CronScheduleBuilder
.cronSchedule("0 0 9 ? * MON-FRI"); // 工作日上午9点
return TriggerBuilder.newTrigger()
.forJob(emailJobDetail())
.withIdentity("emailTrigger")
.withSchedule(schedule)
.build();
}
}
3.3 动态任务调度示例
@Service
public class DynamicTaskService {
@Autowired
private Scheduler scheduler;
// 新增动态任务(如根据配置创建)
public void addDynamicTask(String jobName, String cron)
throws SchedulerException {
JobDetail job = JobBuilder.newJob(DynamicJob.class)
.withIdentity(jobName)
.usingJobData("config", "自定义参数")
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobName + "Trigger")
.withSchedule(CronScheduleBuilder.cronSchedule(cron))
.build();
scheduler.scheduleJob(job, trigger);
}
// 暂停任务(如临时维护)
public void pauseJob(String jobName) throws SchedulerException {
JobKey jobKey = new JobKey(jobName);
scheduler.pauseJob(jobKey);
}
}
public class DynamicJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) {
JobDataMap data = context.getJobDetail().getJobDataMap();
System.out.println("🎛️ 执行动态任务:" + data.getString("config"));
}
}
四、战场景观:应用场景对比
4.1 @Scheduled最佳作战地形
- 简单定时需求(每天数据备份)
- 固定间隔执行(每10分钟检测服务状态)
- 单机环境应用
- 不需要持久化的任务
4.2 Quartz集团军适用战区
- 需要动态调整执行策略
- 分布式集群环境
- 需要失败重试机制
- 任务需要持久化存储
- 复杂调度策略(如月最后一天)
五、装备性能评估:技术优缺点
5.1 @Scheduled突击队优缺点
优点:
- 快速集成(5分钟部署)
- 零配置启动
- 开发成本极低
缺点:
- 单线程执行默认模式
- 不支持动态调整
- 缺乏持久化机制
- 分布式环境下容易重复执行
5.2 Quartz集团军优劣势
优势:
- 支持集群部署
- 完善的失败处理机制
- 可视化调度控制(配合管理界面)
- 灵活的任务持久化方案
劣势:
- 学习曲线较高
- 需要额外数据库支持
- 配置复杂度指数级上升
六、行军备忘录:关键注意事项
- 线程池优化:Quartz默认线程数是10,高并发场景要调整
# application.properties
spring.quartz.properties.org.quartz.threadPool.threadCount=20
- 分布式环境锁机制:使用Redis实现分布式锁
@Scheduled(cron = "0 */5 * * * ?")
public void distributedTask() {
String lockKey = "taskLock";
if(redisLock.tryLock(lockKey, 300)) {
try {
// 执行任务
} finally {
redisLock.unlock(lockKey);
}
}
}
- 异常熔断策略:为任务增加异常重试
public class RetryJob extends QuartzJobBean {
private static final int MAX_RETRY = 3;
@Override
protected void executeInternal(JobExecutionContext context) {
int retryCount = 0;
while(retryCount < MAX_RETRY) {
try {
// 业务逻辑
break;
} catch (Exception e) {
retryCount++;
}
}
}
}
七、参谋部总结
@Scheduled像瑞士军刀,适合快速解决简单问题;Quartz如同专业工具包,能够构建企业级调度系统。选择时需要考虑:是否需要动态调整?执行环境是否分布式?任务是否需要持久化?现在尝试在你的项目中同时使用两种方案:用@Scheduled处理简单心跳检测,用Quartz管理订单超时取消,体验它们的完美配合!
评论