Java实现延时任务案例

wen java案例 2

本文目录导读:

Java实现延时任务案例

  1. 使用 ScheduledExecutorService(推荐)
  2. 使用 Timer / TimerTask(简单场景)
  3. 使用 DelayQueue(任务调度)
  4. 复杂系统:使用 Redis 实现分布式延时任务
  5. 完整业务系统示例
  6. 使用建议
  7. 关键要点

我来详细介绍Java中实现延时任务的几种常见方式,并提供完整案例。

使用 ScheduledExecutorService(推荐)

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class ScheduledExecutorDemo {
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
    private final ScheduledExecutorService scheduler;
    private final AtomicInteger taskCounter = new AtomicInteger(0);
    public ScheduledExecutorDemo(int poolSize) {
        // 创建线程池
        this.scheduler = Executors.newScheduledThreadPool(poolSize, runnable -> {
            Thread thread = new Thread(runnable, "Scheduled-Task-Thread");
            thread.setDaemon(true);
            return thread;
        });
    }
    // 单个延时任务
    public void scheduleDelayedTask(Runnable task, long delay, TimeUnit unit) {
        System.out.println("[" + now() + "] 任务已安排,将在 " + delay + " " + unit + " 后执行");
        scheduler.schedule(() -> {
            try {
                System.out.println("[" + now() + "] 开始执行任务 #" + 
                    taskCounter.incrementAndGet());
                task.run();
            } catch (Exception e) {
                System.err.println("任务执行失败: " + e.getMessage());
            }
        }, delay, unit);
    }
    // 固定频率执行
    public void scheduleAtFixedRate(Runnable task, long initialDelay, 
                                   long period, TimeUnit unit) {
        scheduler.scheduleAtFixedRate(task, initialDelay, period, unit);
    }
    // 固定延迟执行(上次执行完后延迟)
    public void scheduleWithFixedDelay(Runnable task, long initialDelay,
                                      long delay, TimeUnit unit) {
        scheduler.scheduleWithFixedDelay(task, initialDelay, delay, unit);
    }
    // 关闭任务
    public void shutdown() {
        scheduler.shutdown();
        try {
            if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
                scheduler.shutdownNow();
            }
        } catch (InterruptedException e) {
            scheduler.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
    private String now() {
        return LocalDateTime.now().format(FORMATTER);
    }
    // 测试
    public static void main(String[] args) throws InterruptedException {
        ScheduledExecutorDemo demo = new ScheduledExecutorDemo(3);
        // 单次延时任务
        demo.scheduleDelayedTask(() -> 
            System.out.println("[" + demo.now() + "] 这是一次性延时任务"),
            3, TimeUnit.SECONDS);
        // 延迟2秒后,每5秒执行一次
        demo.scheduleAtFixedRate(() -> 
            System.out.println("[" + demo.now() + "] 固定频率任务"),
            2, 5, TimeUnit.SECONDS);
        // 延迟1秒后,每次执行完延迟3秒
        demo.scheduleWithFixedDelay(() -> {
            System.out.println("[" + demo.now() + "] 固定延迟任务开始");
            try {
                Thread.sleep(1000); // 模拟任务执行时间
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }, 1, 3, TimeUnit.SECONDS);
        Thread.sleep(20000);
        demo.shutdown();
    }
}

使用 Timer / TimerTask(简单场景)

import java.util.Timer;
import java.util.TimerTask;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class TimerDemo {
    private static final DateTimeFormatter FORMATTER = 
        DateTimeFormatter.ofPattern("HH:mm:ss");
    public static void main(String[] args) throws InterruptedException {
        Timer timer = new Timer("Timer-Thread", true); // daemon线程
        // 单次延时任务
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("[" + now() + "] 单次延时任务执行");
            }
        }, 3000); // 3秒后执行
        // 固定延迟执行
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println("[" + now() + "] 定时任务执行");
            }
        }, 2000, 5000); // 2秒后开始,每5秒执行一次
        Thread.sleep(15000);
        timer.cancel();
    }
    private static String now() {
        return LocalDateTime.now().format(FORMATTER);
    }
}

使用 DelayQueue(任务调度)

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
// 延时任务类
class DelayedTask implements Delayed {
    private final String taskName;
    private final long executeTime; // 执行时间戳
    private final Runnable task;
    public DelayedTask(String taskName, Runnable task, long delay, TimeUnit unit) {
        this.taskName = taskName;
        this.task = task;
        this.executeTime = System.currentTimeMillis() + 
            TimeUnit.MILLISECONDS.convert(delay, unit);
    }
    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(executeTime - System.currentTimeMillis(), 
            TimeUnit.MILLISECONDS);
    }
    @Override
    public int compareTo(Delayed other) {
        return Long.compare(this.executeTime, 
            ((DelayedTask) other).executeTime);
    }
    public void execute() {
        task.run();
    }
    public String getTaskName() {
        return taskName;
    }
}
public class DelayQueueDemo {
    private static final DateTimeFormatter FORMATTER = 
        DateTimeFormatter.ofPattern("HH:mm:ss");
    private final DelayQueue<DelayedTask> taskQueue = new DelayQueue<>();
    private static final AtomicInteger taskCounter = new AtomicInteger(0);
    public void startWorker() {
        Thread worker = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    DelayedTask task = taskQueue.take();
                    System.out.println("[" + now() + "] 执行任务: " + 
                        task.getTaskName());
                    task.execute();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }, "DelayQueue-Worker");
        worker.setDaemon(true);
        worker.start();
    }
    public void addTask(String name, Runnable task, long delay, TimeUnit unit) {
        DelayedTask delayedTask = new DelayedTask(
            name, task, delay, unit);
        taskQueue.put(delayedTask);
        System.out.println("[" + now() + "] 已添加任务: " + name + 
            ", 延迟: " + delay + " " + unit);
    }
    private String now() {
        return LocalDateTime.now().format(FORMATTER);
    }
    public static void main(String[] args) throws InterruptedException {
        DelayQueueDemo demo = new DelayQueueDemo();
        demo.startWorker();
        // 添加不同延迟的任务
        demo.addTask("任务A", () -> 
            System.out.println("[" + demo.now() + "] 任务A执行完成"),
            3, TimeUnit.SECONDS);
        demo.addTask("任务B", () -> 
            System.out.println("[" + demo.now() + "] 任务B执行完成"),
            1, TimeUnit.SECONDS);
        demo.addTask("任务C", () -> 
            System.out.println("[" + demo.now() + "] 任务C执行完成"),
            5, TimeUnit.SECONDS);
        Thread.sleep(8000);
    }
}

复杂系统:使用 Redis 实现分布式延时任务

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Set;
import java.util.UUID;
public class RedisDelayedTask {
    private static final DateTimeFormatter FORMATTER = 
        DateTimeFormatter.ofPattern("HH:mm:ss");
    private final JedisPool jedisPool;
    private final String taskQueueKey;
    public RedisDelayedTask(String redisHost, int redisPort, String queueName) {
        this.jedisPool = new JedisPool(redisHost, redisPort);
        this.taskQueueKey = queueName;
    }
    // 添加延时任务
    public void addDelayedTask(String taskId, int delaySeconds, String data) {
        try (Jedis jedis = jedisPool.getResource()) {
            long delayTimestamp = System.currentTimeMillis() / 1000 + delaySeconds;
            jedis.zadd(taskQueueKey, delayTimestamp, 
                String.format("%s:%s", taskId, data));
            System.out.println("[" + now() + "] 添加任务: " + taskId + 
                ", 将在 " + delaySeconds + " 秒后执行");
        }
    }
    // 获取到期任务
    public Set<String> getDueTasks() {
        try (Jedis jedis = jedisPool.getResource()) {
            long currentTimestamp = System.currentTimeMillis() / 1000;
            return jedis.zrangeByScore(taskQueueKey, 0, currentTimestamp);
        }
    }
    // 清除已处理任务
    public void removeTask(String task) {
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.zrem(taskQueueKey, task);
        }
    }
    // 启动扫描器
    public void startScanner() {
        Thread scanner = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    Set<String> dueTasks = getDueTasks();
                    for (String task : dueTasks) {
                        processTask(task);
                        removeTask(task);
                    }
                    Thread.sleep(1000); // 每秒检查一次
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                } catch (Exception e) {
                    System.err.println("处理任务时出错: " + e.getMessage());
                }
            }
        }, "Redis-Task-Scanner");
        scanner.setDaemon(true);
        scanner.start();
    }
    private void processTask(String task) {
        System.out.println("[" + now() + "] 执行延时任务: " + task);
        // 在这里执行实际的业务逻辑
    }
    private String now() {
        return LocalDateTime.now().format(FORMATTER);
    }
    public static void main(String[] args) throws InterruptedException {
        // 假设Redis在本地运行
        RedisDelayedTask demo = new RedisDelayedTask("localhost", 6379, "delayed:tasks");
        // 启动扫描器
        demo.startScanner();
        // 添加不同延迟的任务
        for (int i = 1; i <= 5; i++) {
            String taskId = UUID.randomUUID().toString().substring(0, 8);
            demo.addDelayedTask(taskId, i * 2, "任务数据-" + i);
        }
        Thread.sleep(15000);
    }
    // 清理
    public void shutdown() {
        jedisPool.close();
    }
}

完整业务系统示例

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
public class OrderCancelSystem {
    private static final DateTimeFormatter FORMATTER = 
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private final ScheduledExecutorService scheduler;
    private final ExecutorService taskExecutor;
    private final ConcurrentMap<String, ScheduledFuture<?>> taskMap;
    private final AtomicLong orderCounter;
    public OrderCancelSystem() {
        this.scheduler = Executors.newScheduledThreadPool(2);
        this.taskExecutor = Executors.newFixedThreadPool(5);
        this.taskMap = new ConcurrentHashMap<>();
        this.orderCounter = new AtomicLong(0);
    }
    // 创建订单并设置超时自动取消
    public String createOrder(String productId, int quantity, 
                             long timeoutSeconds) {
        String orderId = "ORDER" + 
            String.format("%06d", orderCounter.incrementAndGet());
        System.out.println("[" + now() + "] 创建订单: " + orderId + 
            ", 商品: " + productId + ", " + 
            timeoutSeconds + "秒后未支付将自动取消");
        // 安排过期检测
        OrderCancelTask cancelTask = new OrderCancelTask(orderId);
        ScheduledFuture<?> future = scheduler.schedule(cancelTask, 
            timeoutSeconds, TimeUnit.SECONDS);
        // 存储映射关系,以便后续可能的取消
        taskMap.put(orderId, future);
        return orderId;
    }
    // 取消定时任务(订单已支付)
    public void cancelTask(String orderId) {
        ScheduledFuture<?> future = taskMap.remove(orderId);
        if (future != null) {
            future.cancel(false);
            System.out.println("[" + now() + "] 订单 " + orderId + 
                " 已支付,取消自动关闭");
        }
    }
    // 关闭系统
    public void shutdown() {
        scheduler.shutdownNow();
        taskExecutor.shutdownNow();
        System.out.println("[" + now() + "] 系统已关闭");
    }
    private String now() {
        return LocalDateTime.now().format(FORMATTER);
    }
    private class OrderCancelTask implements Runnable {
        private final String orderId;
        public OrderCancelTask(String orderId) {
            this.orderId = orderId;
        }
        @Override
        public void run() {
            taskMap.remove(orderId); // 清理映射
            // 使用线程池异步执行取消逻辑
            taskExecutor.submit(() -> {
                System.out.println("[" + now() + "] 订单 " + orderId + 
                    " 超时未支付,已自动取消");
                // 模拟取消操作
                try {
                    Thread.sleep(500);
                    System.out.println("[" + now() + "] 订单 " + orderId + 
                        " 取消操作完成,库存已恢复");
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }
    }
    public static void main(String[] args) throws InterruptedException {
        OrderCancelSystem system = new OrderCancelSystem();
        // 创建订单,5秒后自动取消
        String order1 = system.createOrder("商品A", 2, 5);
        String order2 = system.createOrder("商品B", 1, 8);
        Thread.sleep(3000);
        // 用户支付了第一个订单
        System.out.println("[" + system.now() + "] 用户支付订单 " + order1);
        system.cancelTask(order1);
        Thread.sleep(10000);
        // 创建更多订单测试
        String order3 = system.createOrder("商品C", 3, 2);
        Thread.sleep(5000);
        system.shutdown();
    }
}

使用建议

场景 推荐方案 优势
单机应用 ScheduledExecutorService 灵活、高效、线程池管理
简单任务调度 Timer/TimerTask 简单易用
内存级延迟队列 DelayQueue 支持复杂排序
分布式系统 Redis ZSet 支持集群、持久化
企业级调度 Quartz 功能强大、支持CRON表达式

关键要点

  1. 线程安全性:确保任务线程安全
  2. 异常处理:捕获并处理任务异常
  3. 资源管理:及时关闭线程池
  4. 优雅关闭:使用shutdown/shutdownNow方法
  5. 监控告警:添加日志和监控机制

根据实际需求选择合适的方案,注意考虑性能和并发需求。

抱歉,评论功能暂时关闭!