ThreadPool抛出FutureTask cannot be cast to Comparable异常原因咨询
异常触发原因
这个报错和程序挂起是关联问题,根因非常明确:
PriorityBlockingQueue是按优先级排序的阻塞队列,所有存入队列的元素必须实现Comparable接口,或者在构造队列时传入自定义比较器,否则队列在插入元素做排序偏移时,会直接把元素强转为Comparable类型,抛出ClassCastException。- 你是通过
submit()方法向线程池提交任务的:submit()不会把你写的MyThread2实例直接放进队列,它会先把Runnable/Callable任务包装成FutureTask对象,再交给execute()方法入队。JDK原生的FutureTask没有实现Comparable接口,你就算给MyThread2写了Comparable实现也没用,入队的是包装后的FutureTask,自然会触发强转失败。 - 程序挂起是连锁反应:你设置的线程池核心线程数、最大线程数都是2,前两个提交的任务会直接被核心线程占用执行(你的任务是无限循环,没有返回,线程会一直被占用),第三个任务提交时才会尝试进入队列,这时候直接抛异常,
submit()方法异常退出,后面的executor.shutdownNow()根本没机会执行。两个跑着死循环的工作线程收不到中断信号,就会一直运行导致程序挂住。
解决方法
根据实际场景选对应方案即可:
- 如果不需要按优先级调度任务,直接把工作队列换成不需要元素实现比较逻辑的队列即可,比如
new LinkedBlockingQueue<>(),这是普通业务场景最常用的配置,改完就能正常运行,shutdownNow()会正常给线程发中断,打印Terminated!后退出。 - 如果确实需要用优先级队列做任务调度,不要直接用默认无参构造的
PriorityBlockingQueue,需要做两层改造:- 自定义带优先级属性的任务类,实现
Comparable接口定义优先级规则; - 自定义线程池重写
newTaskFor方法,保证包装后的FutureTask子类也携带优先级比较逻辑,不会被队列强转失败。
简单的自定义可比对任务示例:
// 带优先级的可比任务类 abstract class PriorityTask implements Runnable, Comparable<PriorityTask> { private final int priority; public PriorityTask(int priority) { this.priority = priority; } @Override public int compareTo(PriorityTask other) { // 数值越大优先级越高,优先被调度 return Integer.compare(other.priority, this.priority); } } - 自定义带优先级属性的任务类,实现
- 临时测试场景下,也可以把
submit()调用全部换成execute(),同时给MyThread2实现Comparable接口,但这种方式把任务逻辑和队列实现强绑定,线上代码不推荐使用。
注意:
PriorityBlockingQueue是无界队列,搭配线程池使用时,最大线程数参数会失效——因为线程数超过核心线程数后,任务永远会先入无界队列,不会触发非核心线程创建,配置的时候要注意这个坑。
内容的提问来源于stack exchange,提问作者gongliming7
相关产品推荐
相关产品推荐

