应使用PriorityBlockingQueue实现带优先级的并发任务调度,通过自定义比较器或让任务实现Comparable接口来确保排序逻辑,配合守护线程消费任务,并注意线程安全、防饥饿与异常处理。

要用自定义阻塞队列实现带优先级的并发任务调度,关键不是堆砌工具,而是让“谁先执行”这件事由业务规则说了算,并在线程安全前提下稳定落地。
任务对象必须自带排序依据
不能直接往队列里扔 Runnable,它没有优先级字段,也没法比较。得封装一个明确可比的任务类:
- 实现 Comparable
接口,重写 compareTo 方法——比如按 deadline 升序(越早该执行越靠前),deadline 相同时按 priority 降序(数值越大越紧急) - 或者不改任务类,用 PriorityBlockingQueue 构造时传入 lambda 比较器,例如:
(a, b) -> Long.compare(a.getDueTime(), b.getDueTime()) - 避免在任务运行中动态修改 priority 字段,PriorityQueue 不会自动重排;如需调整,得先 remove 再重新 offer
选对底层容器:PriorityBlockingQueue 是主力
它既是优先级队列,又是线程安全的阻塞队列,天然适配生产者-消费者模型:
- 生产者调用 offer() 提交任务,立即返回
- 消费者线程调用 take() 获取任务——若队列为空,线程挂起等待,不空转、不轮询
- 内部使用堆结构,插入和取头都是 O(log n),比遍历找最大优先级高效得多
- 注意:它不保证容量限制,如需防内存溢出,可包装一层容量检查逻辑
调度器主线程要兼顾时效与公平
光有队列不够,得有个持续运转的消费者线程来驱动执行:
- 启动一个守护线程,循环调用 queue.take() 获取最高优任务
- 拿到任务后,判断是否含延迟时间(如 runAtTime):若未到点,可放回队列并 sleep 差值毫秒;更稳妥做法是用 DelayQueue 分离定时逻辑
- 执行任务时建议用 try-catch 包裹,防止单个异常中断整个调度流
- 如需支持取消或超时,任务类应提供 cancel() 或 isCancelled() 方法,但注意 PriorityQueue 本身不支持按条件删除
线程池整合时注意队列替换
如果已有 ThreadPoolExecutor,只需把默认的 LinkedBlockingQueue 替换为 PriorityBlockingQueue:
- 构造时传入自定义比较器,确保任务按 priority 或 deadline 排序
- 设置合理的 corePoolSize(如 1~2),避免高优任务被低优任务挤占线程
- 拒绝策略建议用 CallerRunsPolicy,让提交方自己执行,避免丢任务
- 不要用无界队列 + 大量低优任务,否则高优任务可能长期饥饿










