java.util.concurrent.RejectedExecutionException异常分析

发布时间 2023-12-08 16:58:11作者: 喵师傅

感谢:https://blog.csdn.net/wzy_1988/article/details/38922449

核心池和最大池的大小

graph TB A("提交新任务") -->G{"maximumPoolSize设置为<br/>无界值<br/>(例如:Integer.MAX_VALUE)"} G --- |"无界值"| H["允许线程池适应任意数量的并发任务"] G --- |"有界值"| B{"corePoolSize 和 <br/>maximumPoolSize相同"} B --- |"相同"| C["创建了固定大小的线程池"] B --- |"不同"| D{"运行的线程数:x"} D --- |"x < corePoolSize"| E["创建新线程来处理请求,即使其他辅助线程是空闲的"] D --- |"corePoolSize < x < maximumPoolSize"| F["仅当队列满时才创建新的线程"]

保持活动时间

如果池中当前有多于corePoolSize的线程,则这些多出的线程在空闲时间超过keepAliveTime时将会终止。

排队

所有BlockingQueue都可用于传输和保持提交的任务。可以使用此队列与池大小进行交互:

graph TB A("运行的线程数:x") --> B{"提交任务"} B --- |"x < corePoolSize"| C["Executor始终首选添加新的线程,而不进行排队"] B --- |"x >= corePoolSize"| D["Executor始终首选将请求加入队列,而不添加新的线程"] D --> E{"将请求加入队列"} E --- |"成功"| F["队列+1"] E --- |"失败"| G{"创建新的线程"} G --- |"成功"| I["线程+1"] G --- |"失败"| J["创建此线程超出maximumPoolSize,任务将被拒绝(抛出RejectedExecutionException)"]

排队有三种通用策略:

  1. 直接提交:工作队列的默认选项是synchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界maximumPoolSizes以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增加的可能性。

  2. 无界队列:使用无界队列(例如,不具有预定义容量的LinkedBlockingQueue)将导致在所有corePoolSize线程都忙时新任务在队列中等待。这样,创建的线程就不会超过corePoolSize(因此,maximumPoolSize的值也就无效了)。

  3. 有界队列:当使用有限的maximumPoolSizes时,有界队列(如ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折中:使用大型队列和小型池可以最大限度的降低CPU使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞,则系统可能为超过您许可的更多线程安排时间,使用小型队列通常要求较大的池大小,CPU使用率较高,但是可能遇到不可接受的调度开销,这样可会降低吞吐量。

终止

程序不再引用的池没有剩余线程会自动shutdown。如果希望确保回收取消引用的池(即使用户忘记调用shutdown()),则必须安排未使用的线程最终终止。

异常原因

  1. 线程池显示的调用了shutdown()之后,再向线程池提交任务的时候,如果你配置的拒绝策略是ThreadPoolExecutor.AbortPolicy的话,这个异常就被会抛出来。

  2. 当你的排队策略为有界队列,并且配置的拒绝策略是ThreadPoolExecutor.AbortPolicy,当线程池的线程数量已经达到了maximumPoolSize的时候,你再向它提交任务,就会抛出ThreadPoolExecutor.AbortPolicy异常。

解决方案

  1. 尽量调大maximumPoolSize,例如设置为Integer.MAX_VALUE
  2. 使用其他排队策略,例如LinkedBlockingQueue