Java的各类定时任务实现

在 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() {
            @Override
            public 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 {
        @Override
        public 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 {
    
    @Bean
    public JobDetail sampleJobDetail() {
        return JobBuilder.newJob(SampleQuartzJob.class)
                .withIdentity("sampleJob")
                .storeDurably()
                .build();
    }
    
    @Bean
    public 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 {
    
    @Override
    protected 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 {
    
    @Autowired
    private 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,根据项目复杂度选择合适的方案。

posted @ 2025-10-11 15:01  dirgo  阅读(121)  评论(0)    收藏  举报