在 Java 中实现定时任务有多种方式,下面详细介绍各种方法的使用:
1. Timer 和 TimerTask(基础版)
import java.util.Timer;
import java.util.TimerTask;public class TimerExample {public static void main(String[] args) {Timer timer = new Timer();// 创建定时任务TimerTask task = new TimerTask() {@Overridepublic void run() {System.out.println("定时任务执行了,时间: " + new Date());}};// 安排任务执行// 延迟1秒后执行,然后每5秒执行一次timer.schedule(task, 1000, 5000);// 在特定时间执行// Calendar calendar = Calendar.getInstance();// calendar.add(Calendar.SECOND, 10);// timer.schedule(task, calendar.getTime());// 10秒后停止定时器try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}timer.cancel();}
}
2. ScheduledExecutorService(推荐)
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;public class ScheduledExecutorExample {public static void main(String[] args) {// 创建调度线程池ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);// 任务1:固定延迟执行Runnable task1 = () -> {System.out.println("任务1执行 - " + Thread.currentThread().getName() + " - " + new Date());};// 任务2:带返回值的任务ScheduledFuture<String> task2 = scheduler.schedule(() -> {System.out.println("一次性任务执行");return "任务完成";}, 3, TimeUnit.SECONDS);// 安排任务执行// 延迟2秒后开始执行,然后每5秒执行一次(固定延迟)ScheduledFuture<?> scheduledTask = scheduler.scheduleWithFixedDelay(task1, 2, 5, TimeUnit.SECONDS);// 固定频率执行(不考虑任务执行时间)// scheduler.scheduleAtFixedRate(task1, 2, 5, TimeUnit.SECONDS);try {// 运行20秒Thread.sleep(20000);// 取消任务scheduledTask.cancel(false);// 获取一次性任务的结果if (task2.isDone()) {System.out.println("一次性任务结果: " + task2.get());}} catch (Exception e) {e.printStackTrace();} finally {// 关闭调度器scheduler.shutdown();try {if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {scheduler.shutdownNow();}} catch (InterruptedException e) {scheduler.shutdownNow();}}}
}
3. Spring Framework 的 @Scheduled(最常用)
配置类或者主启动类上添加 @EnableScheduling注解
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;@Configuration
@EnableScheduling
public class SpringSchedulerConfig {// 启用定时任务支持
}
定时任务类添加@Component
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;@Component
public class SpringScheduledTasks {/*** 固定速率执行 - 每5秒执行一次* 从上一次开始时间计算*/@Scheduled(fixedRate = 5000)public void fixedRateTask() {System.out.println("固定速率任务 - " + LocalDateTime.now());}/*** 固定延迟执行 - 上一次执行完成后延迟3秒再执行*/@Scheduled(fixedDelay = 3000)public void fixedDelayTask() {try {// 模拟任务执行时间TimeUnit.SECONDS.sleep(1);System.out.println("固定延迟任务 - " + LocalDateTime.now());} catch (InterruptedException e) {Thread.currentThread().interrupt();}}/*** 初始延迟 - 应用启动后延迟10秒开始执行,然后每30秒执行一次*/@Scheduled(initialDelay = 10000, fixedRate = 30000)public void initialDelayTask() {System.out.println("初始延迟任务 - " + LocalDateTime.now());}/*** Cron 表达式 - 更灵活的时间控制*/@Scheduled(cron = "0 */2 * * * ?") // 每2分钟执行一次public void cronTask() {System.out.println("Cron任务执行 - " + LocalDateTime.now());}/*** Cron 表达式示例*/@Scheduled(cron = "0 0 12 * * ?") // 每天中午12点执行public void noonTask() {System.out.println("中午任务执行 - " + LocalDateTime.now());}/*** 支持占位符,从配置文件中读取*/@Scheduled(cron = "${task.cron.expression:0 */5 * * * ?}")public void configurableTask() {System.out.println("可配置任务 - " + LocalDateTime.now());}
}
4. Quartz Scheduler(企业级)
添加依赖(Maven)
<dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>2.3.2</version>
</dependency>
Quartz 配置
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;public class QuartzExample {// 定义任务public static class SimpleJob implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("Quartz任务执行 - " + new Date());// 获取传递的参数JobDataMap dataMap = context.getJobDetail().getJobDataMap();String message = dataMap.getString("message");if (message != null) {System.out.println("消息: " + message);}}}public static void main(String[] args) throws SchedulerException {// 创建调度器Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();// 定义任务详情JobDetail job = JobBuilder.newJob(SimpleJob.class).withIdentity("simpleJob", "group1").usingJobData("message", "Hello Quartz!").build();// 定义触发器Trigger trigger = TriggerBuilder.newTrigger().withIdentity("simpleTrigger", "group1").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(10).repeatForever()).build();// 安排任务scheduler.scheduleJob(job, trigger);// 启动调度器scheduler.start();// 运行一段时间后关闭try {Thread.sleep(60000);} catch (InterruptedException e) {e.printStackTrace();}scheduler.shutdown();}
}
5. 基于 Spring + Quartz 的集成
配置类
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class QuartzConfig {@Beanpublic JobDetail sampleJobDetail() {return JobBuilder.newJob(SampleQuartzJob.class).withIdentity("sampleJob").storeDurably().build();}@Beanpublic Trigger sampleJobTrigger() {SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(30).repeatForever();return TriggerBuilder.newTrigger().forJob(sampleJobDetail()).withIdentity("sampleTrigger").withSchedule(scheduleBuilder).build();}
}
任务类
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;public class SampleQuartzJob extends QuartzJobBean {@Overrideprotected void executeInternal(JobExecutionContext context) throws JobExecutionException {System.out.println("Spring Quartz 任务执行 - " + new Date());}
}
6. Cron 表达式详解
/*** Cron 表达式格式: [秒] [分] [时] [日] [月] [周] [年] (年可选)* * 常用示例:* "0 0 12 * * ?" 每天12点* "0 15 10 ? * *" 每天10:15* "0 0/5 14,18 * * ?" 每天14点和18点,每5分钟一次* "0 0-5 14 * * ?" 每天14:00到14:05每分钟执行* "0 0/30 9-17 * * ?" 朝九晚五工作时间内每半小时* "0 0 12 ? * WED" 每周三中午12点* "0 15 10 15 * ?" 每月15日上午10:15*/
7. 最佳实践和注意事项
异常处理
@Component
public class SafeScheduledTask {@Scheduled(fixedRate = 5000)public void safeTask() {try {// 业务逻辑performBusinessLogic();} catch (Exception e) {// 记录日志,但不抛出异常,避免任务停止System.err.println("定时任务执行失败: " + e.getMessage());// 可以添加告警逻辑}}private void performBusinessLogic() {// 模拟业务逻辑if (Math.random() < 0.1) {throw new RuntimeException("模拟异常");}System.out.println("业务逻辑执行成功");}
}
分布式环境考虑
@Component
public class DistributedScheduledTask {@Autowiredprivate DistributedLockService lockService;@Scheduled(cron = "0 */5 * * * ?")public void distributedTask() {String lockKey = "distributed_task_lock";if (lockService.tryLock(lockKey, 300)) { // 锁定5分钟try {// 执行任务System.out.println("获取分布式锁,执行任务");// 业务逻辑...} finally {lockService.unlock(lockKey);}} else {System.out.println("未获取到锁,跳过执行");}}
}
总结对比
方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
Timer | 简单易用 | 单线程,任务阻塞 | 简单的单机任务 |
ScheduledExecutorService | 线程池管理,更灵活 | 配置相对复杂 | 需要精细控制的定时任务 |
Spring @Scheduled | 配置简单,集成方便 | 功能相对基础 | Spring项目中的常规定时任务 |
Quartz | 功能强大,支持集群 | 配置复杂,依赖多 | 企业级复杂调度需求 |
Spring + Quartz | 结合两者优势 | 配置较复杂 | 需要Quartz功能的Spring项目 |
推荐使用顺序:Spring @Scheduled → ScheduledExecutorService → Quartz,根据项目复杂度选择合适的方案。