Java线程池抛出异常 java.util.concurrent.RejectedExecutionException 是配置的问题吗?

异常内容如下

java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@174a9357 rejected from java.util.concurrent.ThreadPoolExecutor@68ab7098[Running, pool size = 160, active threads = 160, queued tasks = 10000, completed tasks = 588179]
        at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
        at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
        at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
        at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
        at com.test.TcpServerHandler.channelRead(TcpServerHandler.java:71)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
        at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1359)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
        at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:935)
        at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:134)
        at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:645)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:580)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:497)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:459)

线程池配置如下

new ThreadPoolExecutor(processNum * 10, processNum * 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(10000), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy())

服务器是8核16线程的

异常后重启一段时间还是会重现,每次都是 completed tasks = 588179 这个数字时抛异常,哪位大神有解决方案,求指教

阅读 3.1k
7 个回答

这个异常是触发了线程池的拒绝策略导致的, 你需要大致了解一下线程池的线程调度逻辑。
线程池中有几个核心参数:

  1. 核心线程数(corePoolSize)
  2. 最大线程数(maximumPoolSize)
  3. 等待队列(BlockingQueue<Runnable> workQueue)
  4. 拒绝策略(RejectedExecutionHandler handler)。

提交的任务将先使用核心线程数, 但超过核心线程数, 则存入等待队列中, 当等待队列满了, 则创建小于等于最大线程数的线程执行任务, 当最大线程数也不足以容纳新的任务时将出发拒绝策略。
可用的拒绝策略有:

  1. ThreadPoolExecutor.AbortPolicy(默认):当线程池无法接受新任务时,抛出RejectedExecutionException异常,阻止任务提交。题主所触发的策略
  2. ThreadPoolExecutor.CallerRunsPolicy:当线程池无法接受新任务时,由提交任务的线程来执行被拒绝的任务。这种策略可以降低新任务的流量,但也可能导致整体性能下降。可以尝试使用的策略
  3. ThreadPoolExecutor.DiscardPolicy:当线程池无法接受新任务时,直接丢弃被拒绝的任务,没有任何异常抛出。
  4. ThreadPoolExecutor.DiscardOldestPolicy:当线程池无法接受新任务时,丢弃线程池队列中最旧的未处理任务,然后尝试重新提交被拒绝的任务。视业务场景可以使用的策略

目测不是配置问题,就是堆积的任务太多了处理不过来了
new LinkedBlockingQueue(10000) 最大积压10000个任务
new ThreadPoolExecutor.AbortPolicy() 积压满了就拒绝服务
解决方案:

  1. 观察任务积压情况,排查积压原因
  2. 改善积压情况

    1. 增加线程池大小
    2. 优化代码提高处理速度
  3. 提高容量

    1. 增加队列长度
    2. 增加更多服务实例
新手上路,请多包涵

java.util.concurrent.RejectedExecutionException这个异常是在任务无法由Executor执行时抛出的。这通常是因为Executor已经被关闭,或者因为它的任务队列已经满了。

这个帖子可以看看:https://blog.csdn.net/wzy_1988/article/details/38922449
解决方案:

  1. 尽量调大maximumPoolSize,例如设置为Integer.MAX_VALUE

    public ExecutorService customerExecutorService = new ThreadPoolExecutor(3, Integer.MAX_VALUE, 0, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>());
  2. 使用其他排队策略,例如LinkedBlockingQueue

    <span style="white-space:pre">    </span>public ExecutorService customerExecutorService = new ThreadPoolExecutor(3, 5, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());

    企业微信截图_1686216452442.png

1万的队列都扛不住,你的那些任务是有多耗时

  • 如果cpu利用率还有空余,调大核心线程数,processNum * 50
  • 如果cpu已经满了,只能优化你的任务逻辑,减少单次任务的耗时
  • 如果任务无法优化,说明你的机器性能已经达到负载极限了,加机器

根据给出的内容,线程池还在running,但是pool已经满了(160),然后queue也满了(10000),所以新submit的task被abort策略拒绝了...
看比例,160运行:10000等待,说明单个任务执行时间太长了,处理不过来了...

每次都是 completed tasks = 588179 这个数字时抛异常

这可能说明你每个任务的执行时间都很恒定,所以导致每次到这个时候(588179+160+10000),线程池就满了。
我建议首先检查一下任务的处理逻辑,看看代码有没有什么问题(之前遇到过线程任务里面有大循环的,有sleep的,有互相锁数据的,有调用长耗时三方接口的),打日志,观察单个任务的执行时间,优化代码的耗时,或者从业务角度调整相关逻辑...
然后还可以加mq一类的消息队列做消费端接耦,实在不行就加资源,加线程,加硬件...

答案:根据你的线程池参数配置,抛出这个exception是正确的。抛异常原因是待处理任务数量到达了配置的10000个(new LinkedBlockingQueue(10000)),这时候再来一个待处理的任务,根据你配置的拒绝策略(new ThreadPoolExecutor.AbortPolicy()),这时候,就回抛出题中的异常。
解决办法:方向一:你这个任务明显是耗时任务,考虑加入mq或者数据库先把待处理的任务记录到db,慢慢处理,替换线程池的方案。
方向二:判断任务耗时是否正常,可否优化耗时时间,减少任务堆积。

推荐问题
宣传栏