如何设计一个任务队列,支持优先级区分、取消任务
设计一个任务队列,支持优先级区分和取消任务,可以使用优先级队列(如 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 和状态管理取消功能,结合线程池消费任务,确保高效和安全。面试时,可写核心代码或提优化点,展示设计能力。