一、背景
前面一篇文章转载了 ThreadPoolExecutor的几种拒绝策略:https://blog.csdn.net/w605283073/article/details/89930154,讲得很不错。
但是网上介绍使用拒绝策略遇到的大坑的文章不多。
本文介绍其中一个真是故障的场景。
二、问题描述
ThreadPoolExecutor的拒绝策略常用的是DiscardPolicy或者DiscardOldestPolicy。
但是有个应用使用的拒绝策略是CallerRunsPolicy。
Java手册对这个策略的描述是:
handler for rejected tasks that runs the rejected task directly in the calling thread of the execute method, unless the executor has been shut down, in which case the task is discarded.
CallerRunsPolicy在任务被拒绝添加后,会在调用execute方法的的线程来执行被拒绝的任务。
除非executor被关闭,否则任务不会被丢弃。
如果最大线程池设置较小,而且工作队列数不够大,由于代码Bug等导致任务量激增。
任务会交个上层线程(主线程)执行,导致主线程既要处理其他任务,又要忙碌处理线程池的源源不断的大量任务,导致hang住。
进而导致线上故障。
因此我们创建新线程池的时候,要考虑线程数量,队列的长度,有使用CachedThreadPool 和 ScheduledThreadPool线程池,由于代码bug,导致创建大量的线程,从而导致线上OOM的故障。
《阿里巴巴Java开发手册》的提示不容忽略:
【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样
的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors 返回的线程池对象的弊端如下:
1)FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2)CachedThreadPool 和 ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
还要想清楚使用的拒绝策略带来的风险。