线程池RejectedExecutionException异常
文章目录
- 1、报错
- 2、定位
- 3、修复
- 4、线程池使用的一点思考
1、报错
检索项目日志时,发现一个异常堆栈信息,核心报错:
java.util.concurrent.RejectedExecutionException:
Task java.util.concurrent.CompletableFuture$AsyncSupply@480a10c7
rejected from java.util.concurrent.ThreadPoolExecutor@51313448
[Running, pool size = 32, active threads = 32, queued tasks = 200, completed tasks = 2487]
// ...
关键字:RejectedExecutionException
,从这个异常可以看出,这是因为往线程池提交的任务数量超过了最大线程数 + 阻塞队列长度,然后任务再提交过来,线程池无法再接收新的任务,走了默认的拒绝策略 AbortPoligy:直接抛出RejectedExecutionExption异常阻止系统正常运行
2、定位
查看堆栈信息里的信息,定位到异常代码出自:
@Service
public class InspectionService {private static final Logger log = LoggerFactory.getLogger(InspectionService.class);private final ExecutorService pool = new ThreadPoolExecutor(16, // 常驻线程数量32, // 最大线程数量10, // 线程存活时间,线程多长时间没被使用就关闭TimeUnit.SECONDS, // 上一个参数的单位new LinkedBlockingQueue<>(200), // 阻塞队列new ThreadFactoryBuilder().setNameFormat("InspectionService-%d").build(), // 线程工厂对象new ThreadPoolExecutor.AbortPolicy() // 拒绝策略);
// ....
再下面CompleteableFuture再提交任务到线程池:
CompletableFuture.supplyAsync(() -> service.queryGroupInfo(), pool)
//....
提交速度 >>
线程池处理速度
3、修复
调大了阻塞队列的长度:
private final ExecutorService pool = new ThreadPoolExecutor(16,32,10,TimeUnit.SECONDS,// groupInfos.size < 500, 这里取length * 1.5new LinkedBlockingQueue<>(700),new ThreadFactoryBuilder().setNameFormat("InspectionService-%d").build(),new ThreadPoolExecutor.AbortPolicy()
);
4、线程池使用的一点思考
- 线程池自己定义,JDK中静态方法创建的阻塞队列长度为Long.MAX_VALUE,风险很高
参数 | 设置要点 | 示例 |
---|---|---|
corePoolSize | CPU密集型:N+1(N=CPU核数)IO密集型:2N | 8核服务器:CPU密集型设9,IO密集型设16 |
maximumPoolSize | 不超过系统最大线程数(cat /proc/sys/kernel/threads-max) | 通常设置 coreSize 的 2-4 倍 |
blockQueue | 有界队列!无界队列=内存炸弹 | new LinkedBlockingQueue<>(200) |
keepAliveTime | 线程空闲回收时间(建议30-60秒) | TimeUnit.SECONDS + 10 |
- 线程池中的线程带名字,不要用默认的pool-xxx,出现问题不好定位是哪儿的任务
- 可以加必要监控,采集日志分析,到达阈值时告警处理
// 定时采集关键指标(每30秒)
// 单例调度线程池ScheduledExecutorService monitor = Executors.newSingleThreadScheduledExecutor();monitor.scheduleAtFixedRate(() -> {if (pool instanceof ThreadPoolExecutor executor) {int activeCount = executor.getActiveCount();int queueSize = executor.getQueue().size();long completedTaskCount = executor.getCompletedTaskCount();log.info("线程池状态:活跃线程:{} 队列堆积:{} 完成数:{}", activeCount, queueSize, completedTaskCount);if (queueSize > 500) {log.warn("线程池队列堆积过多,队列堆积:{} 完成数:{}", queueSize, completedTaskCount);}}}, 0, 30, TimeUnit.SECONDS);}
- 核心业务与非核心业务使用独立线程池,做到资源隔离