问题分析
java.util.concurrent.RejectedExecutionException
是 Java 并发编程中常见的异常之一,它通常发生在使用 ExecutorService
(如 ThreadPoolExecutor
)执行任务时,当任务无法被提交到线程池执行时就会抛出此异常。这通常意味着线程池已经达到其最大容量,或者已经关闭,无法再接受新的任务。
报错原因
-
线程池已满:当线程池中的线程数量已经达到其配置的最大值(
corePoolSize
+maximumPoolSize
),并且工作队列也满了,此时再尝试提交新任务就会抛出RejectedExecutionException
。 -
线程池已关闭:如果线程池已经被
shutdown()
或shutdownNow()
方法关闭,并且已经执行完所有任务或者等待的任务被中断,此时再提交新任务也会抛出此异常。
解决思路
-
调整线程池参数:根据应用的实际情况,合理调整线程池的大小(
corePoolSize
、maximumPoolSize
)、工作队列的大小以及拒绝策略等参数。 -
优化任务处理逻辑:减少任务的执行时间,提高任务处理效率,避免大量任务长时间占用线程池资源。
-
使用其他并发策略:如果线程池不适合当前的应用场景,可以考虑使用其他并发策略,如使用
ForkJoinPool
、CompletableFuture
或Actor
模型等。 -
处理拒绝策略:为线程池配置合适的拒绝策略,如使用
ThreadPoolExecutor.CallerRunsPolicy
策略,让提交任务的线程自己执行该任务。
解决方法
下滑查看解决方法
方法一:调整线程池参数
// 假设原来的线程池配置不合理,现在进行调整
ExecutorService executorService = new ThreadPoolExecutor(
5, // corePoolSize
10, // maximumPoolSize
60, // keepAliveTime
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100) // 工作队列大小
);
// ... 使用 executorService 提交任务
// 当不再需要线程池时,关闭它
executorService.shutdown();
方法二:优化任务处理逻辑
这通常涉及到代码逻辑的优化,比如减少任务执行时间、避免不必要的阻塞操作等。这里不直接给出代码示例,因为具体实现取决于应用的具体需求。
方法三:使用其他并发策略
使用 ForkJoinPool
示例:
ForkJoinPool forkJoinPool = new ForkJoinPool();
forkJoinPool.submit(() -> {
// 执行任务的代码
return null;
});
// 当不再需要 ForkJoinPool 时,可以调用 shutdown 方法
forkJoinPool.shutdown();
方法四:处理拒绝策略
为线程池配置 CallerRunsPolicy
拒绝策略:
ExecutorService executorService = new ThreadPoolExecutor(
5, // corePoolSize
10, // maximumPoolSize
60, // keepAliveTime
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100), // 工作队列大小
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
// ... 使用 executorService 提交任务
// 当不再需要线程池时,关闭它
executorService.shutdown();
在这个配置下,如果线程池无法接受新的任务,将会由提交任务的线程自己来执行该任务。这样可以避免抛出 RejectedExecutionException
,但也可能导致提交任务的线程被阻塞,从而影响应用的响应性能。因此,在选择拒绝策略时需要权衡利弊。