使用 Java 实现一个简单且高效的任务调度框架

使用 Java 实现一个简单且高效的任务调度框架(2026年实用版)

任务调度框架是后台系统中的核心组件,用于管理定时任务、延迟任务、周期任务等。Java 生态中已有 Quartz、Spring Task 等成熟框架,但如果你想从零实现一个简单、高效的版本(适合学习/小项目),可以基于 java.util.concurrent 包构建——它原生支持线程池、多线程调度,避免从头造轮子。

设计原则(保持简单高效):

  • 核心功能:支持一次性任务、固定延迟任务、固定频率任务、任务取消。
  • 高效点:使用 ScheduledThreadPoolExecutor 作为底层引擎(JDK内置,高性能,支持优先级队列)。
  • 简单点:最小接口设计,无外部依赖,仅需 JDK 8+。
  • 局限:不支持持久化/分布式(如果需要,可扩展到 Redis/Zookeeper),不处理异常重试(可加装饰器)。
  • 适用场景:小中型应用、原型开发、面试手写题。

总体架构(用表格表示,便于理解)

组件描述实现方式
Task任务接口(Runnable 扩展,支持优先级/元数据)自定义接口,包含 id/priority
TaskScheduler调度器接口(提交任务、调度任务、取消任务)核心类,使用 ScheduledExecutorService
PriorityQueue内部队列(支持优先级调度)PriorityBlockingQueue
ThreadPool执行引擎(线程复用,避免频繁创建线程)ScheduledThreadPoolExecutor
示例扩展可加日志、监控(e.g., JMX)、异常处理通过装饰器模式

核心代码实现(完整、可直接复制运行)

  1. 任务接口(SimpleTask.java)
    支持基本元数据,便于追踪/取消。
   import java.util.UUID;

   public interface SimpleTask extends Runnable {
       String getId();      // 唯一ID,用于取消
       int getPriority();   // 优先级(越小越高,默认0)
   }

   // 默认实现(方便用户继承)
   abstract class AbstractSimpleTask implements SimpleTask {
       private final String id = UUID.randomUUID().toString();
       private final int priority;

       public AbstractSimpleTask(int priority) {
           this.priority = priority;
       }

       @Override
       public String getId() {
           return id;
       }

       @Override
       public int getPriority() {
           return priority;
       }
   }
  1. 调度器实现(SimpleTaskScheduler.java)
    核心类,封装 ScheduledExecutorService。
   import java.util.concurrent.*;
   import java.util.Map;
   import java.util.HashMap;

   public class SimpleTaskScheduler {
       private final ScheduledExecutorService executor;
       private final Map<String, ScheduledFuture<?>> taskMap = new HashMap<>();  // 追踪任务,便于取消

       public SimpleTaskScheduler(int corePoolSize) {
           executor = Executors.newScheduledThreadPool(corePoolSize);
       }

       // 提交一次性任务(立即执行)
       public void submit(SimpleTask task) {
           executor.execute(task);
       }

       // 调度延迟任务(delay 后执行一次)
       public void schedule(SimpleTask task, long delay, TimeUnit unit) {
           ScheduledFuture<?> future = executor.schedule(task, delay, unit);
           taskMap.put(task.getId(), future);
       }

       // 调度固定频率任务(initialDelay 后,每 period 执行一次)
       public void scheduleAtFixedRate(SimpleTask task, long initialDelay, long period, TimeUnit unit) {
           ScheduledFuture<?> future = executor.scheduleAtFixedRate(task, initialDelay, period, unit);
           taskMap.put(task.getId(), future);
       }

       // 调度固定延迟任务(initialDelay 后,每次执行完后延迟 delay 执行下一次)
       public void scheduleWithFixedDelay(SimpleTask task, long initialDelay, long delay, TimeUnit unit) {
           ScheduledFuture<?> future = executor.scheduleWithFixedDelay(task, initialDelay, delay, unit);
           taskMap.put(task.getId(), future);
       }

       // 取消任务
       public boolean cancel(String taskId) {
           ScheduledFuture<?> future = taskMap.remove(taskId);
           if (future != null) {
               return future.cancel(false);  // false: 不中断正在运行的任务
           }
           return false;
       }

       // 优雅关闭调度器
       public void shutdown() {
           executor.shutdown();
           try {
               if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                   executor.shutdownNow();
               }
           } catch (InterruptedException e) {
               executor.shutdownNow();
           }
       }
   }
  1. 示例使用(Main.java)
    测试各种调度场景。
   public class Main {
       public static void main(String[] args) {
           SimpleTaskScheduler scheduler = new SimpleTaskScheduler(4);  // 4 核心线程

           // 一次性任务
           SimpleTask oneTimeTask = new AbstractSimpleTask(0) {
               @Override
               public void run() {
                   System.out.println("一次性任务执行: " + System.currentTimeMillis());
               }
           };
           scheduler.submit(oneTimeTask);

           // 延迟任务(5秒后执行)
           SimpleTask delayedTask = new AbstractSimpleTask(1) {
               @Override
               public void run() {
                   System.out.println("延迟任务执行: " + System.currentTimeMillis());
               }
           };
           scheduler.schedule(delayedTask, 5, TimeUnit.SECONDS);
           String delayedId = delayedTask.getId();  // 保存ID,用于可能取消

           // 固定频率任务(立即开始,每2秒执行一次)
           SimpleTask fixedRateTask = new AbstractSimpleTask(2) {
               @Override
               public void run() {
                   System.out.println("固定频率任务: " + System.currentTimeMillis());
               }
           };
           scheduler.scheduleAtFixedRate(fixedRateTask, 0, 2, TimeUnit.SECONDS);

           // 固定延迟任务(1秒后开始,每次执行完后延迟3秒)
           SimpleTask fixedDelayTask = new AbstractSimpleTask(3) {
               @Override
               public void run() {
                   System.out.println("固定延迟任务开始: " + System.currentTimeMillis());
                   try { Thread.sleep(1000); } catch (InterruptedException e) {}  // 模拟耗时1秒
                   System.out.println("固定延迟任务结束: " + System.currentTimeMillis());
               }
           };
           scheduler.scheduleWithFixedDelay(fixedDelayTask, 1, 3, TimeUnit.SECONDS);

           // 模拟运行10秒后取消一个任务
           try { Thread.sleep(10000); } catch (InterruptedException e) {}
           scheduler.cancel(delayedId);  // 取消延迟任务(如果还没执行)

           // 关闭调度器
           scheduler.shutdown();
       }
   }

性能优化 & 扩展建议(高效点)

  • 线程池大小:根据 CPU 核数设置(e.g., Runtime.getRuntime().availableProcessors() * 2)。
  • 优先级支持:当前已支持(通过 AbstractSimpleTask 的 priority),但底层 ScheduledExecutorService 默认不按优先级调度——如果需要,可替换为 PriorityBlockingQueue + 自定义 Comparator。
  • 异常处理:在 run() 中加 try-catch,记录日志(e.g., SLF4J)。
  • 监控:集成 Micrometer/Prometheus,暴露线程池指标。
  • 分布式扩展:用 Redis 作为任务队列 + 锁(Redisson),实现多节点调度。
  • 测试效率:在单元测试中,用 mock ExecutorService 模拟时间推进(避免真实 sleep)。

运行 Main.java,你会看到控制台输出各种任务的执行时间戳。简单吧?这个框架参数少、开销低(JDK 原生),实际项目中可作为起点扩展。

如果你想加特定功能(e.g., Cron 表达式支持、持久化),或遇到报错,告诉我,我可以给你更详细的代码调整~

文章已创建 3806

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部