Skip to content

如何设计一个任务队列,支持优先级区分、取消任务

设计一个任务队列,支持优先级区分和取消任务,可以使用优先级队列(如 PriorityBlockingQueue存储任务,结合任务标识(如 ID)状态管理实现取消功能。核心思路是: - 优先级区分:任务带优先级字段,队列按优先级排序。 - 取消任务:通过唯一 ID 标记任务,支持移除或标记取消。


1. 设计思路

核心组件

  • 任务类(Task)
  • 属性:任务 ID、优先级、内容、状态(待执行、已取消)。
  • 任务队列
  • 使用优先级队列(如 PriorityBlockingQueue)存储任务,按优先级排序。
  • 任务管理器
  • 提供添加、取消、执行接口。
  • 线程池
  • 消费队列,执行任务。

功能要求

  • 优先级区分:高优先级任务先执行。
  • 取消任务:支持运行前或运行中取消。

2. 实现方案

(1) 任务类设计

public class Task implements Comparable<Task> {
    private String id;         // 唯一标识
    private int priority;      // 优先级(值越小越优先)
    private Runnable action;   // 任务内容
    private volatile boolean cancelled; // 取消状态

    public Task(String id, int priority, Runnable action) {
        this.id = id;
        this.priority = priority;
        this.action = action;
        this.cancelled = false;
    }

    public String getId() { return id; }
    public boolean isCancelled() { return cancelled; }
    public void cancel() { this.cancelled = true; }

    @Override
    public int compareTo(Task other) {
        return Integer.compare(this.priority, other.priority); // 升序,小值优先
    }

    public void run() {
        if (!cancelled) {
            action.run();
        }
    }
}

(2) 任务队列管理器

public class TaskQueueManager {
    // 优先级阻塞队列
    private final PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();
    // 任务映射,用于快速查找
    private final ConcurrentHashMap<String, Task> taskMap = new ConcurrentHashMap<>();
    // 线程池
    private final ExecutorService executor = Executors.newFixedThreadPool(4);

    public TaskQueueManager() {
        startConsumer(); // 启动消费线程
    }

    // 添加任务
    public void addTask(String id, int priority, Runnable action) {
        Task task = new Task(id, priority, action);
        taskMap.put(id, task);
        queue.offer(task);
    }

    // 取消任务
    public boolean cancelTask(String id) {
        Task task = taskMap.get(id);
        if (task == null) return false;
        task.cancel(); // 标记取消
        boolean removed = queue.remove(task); // 尝试从队列移除
        taskMap.remove(id);
        return removed || task.isCancelled(); // 已移除或已标记取消
    }

    // 消费任务
    private void startConsumer() {
        executor.submit(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    Task task = queue.take(); // 阻塞获取任务
                    if (!task.isCancelled()) {
                        task.run(); // 执行任务
                    }
                    taskMap.remove(task.getId()); // 完成后移除
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        });
    }

    // 关闭
    public void shutdown() {
        executor.shutdown();
    }
}

(3) 使用示例

public class Main {
    public static void main(String[] args) throws InterruptedException {
        TaskQueueManager manager = new TaskQueueManager();

        // 添加任务
        manager.addTask("t1", 2, () -> System.out.println("Task 1, priority 2"));
        manager.addTask("t2", 1, () -> System.out.println("Task 2, priority 1"));
        manager.addTask("t3", 3, () -> System.out.println("Task 3, priority 3"));

        // 取消任务
        Thread.sleep(100); // 模拟延迟
        manager.cancelTask("t3");

        Thread.sleep(1000); // 等待执行
        manager.shutdown();
    }
}
  • 输出(可能):
Task 2, priority 1
Task 1, priority 2

3. 实现细节

(1) 优先级区分

  • 数据结构PriorityBlockingQueue 内部用堆排序,优先级小的先出队。
  • 自定义优先级Task 实现 Comparable,按 priority 排序。

(2) 取消任务

  • 运行前取消:通过 queue.remove(task) 移除。
  • 运行中取消:用 volatile cancelled 标记,任务执行前检查。
  • 任务查找ConcurrentHashMap 快速定位任务。

(3) 并发安全

  • 队列PriorityBlockingQueue 线程安全。
  • 任务状态volatile 确保取消可见。
  • 任务映射ConcurrentHashMap 支持并发操作。

4. 优缺点

优点

  • 优先级支持:高优先级任务优先执行。
  • 取消灵活:支持运行前和运行中取消。
  • 扩展性:易添加超时、重试等功能。

缺点

  • 性能开销:维护 taskMap 和队列同步有成本。
  • 取消局限:运行中任务需主动检查状态。

5. 延伸与面试角度

  • 优化
  • 批量处理:队列积攒任务后批量执行。
  • 超时控制:任务带超时字段,过期移除。
  • 替代方案
  • ThreadPoolExecutor:自定义 PriorityBlockingQueue
  • ScheduledExecutorService:支持延迟和优先级。
  • 实际应用
  • 消息处理:优先级高的消息先消费。
  • 任务调度:如爬虫任务取消。
  • 面试点
  • 问“优先级”时,提堆排序。
  • 问“取消”时,提状态标记。

总结

通过 PriorityBlockingQueue 实现优先级区分,用 Task 的 ID 和状态管理取消功能,结合线程池消费任务,确保高效和安全。面试时,可写核心代码或提优化点,展示设计能力。