问题描述

在线上发生的一次问题, 在场景中有这样一个业务, 需要异步执行一个主任务, 主任务中又包含着N个子任务, 为了整个主任务能够快速处理, 又将子任务按照数量获取线程资源异步处理, 即异步线程A中再异步调用A1,A2,A3. A可能同时存在多个.实际场景中, 由于系统线程池分配数量较小, 且一段时间内先后启动了多个主任务, 耗时的主任务中又用子任务取申请线程 导致线程池资源耗尽

问题原因

1. 主任务是从线程池中获取的线程资源, 同时主任务比较耗时​
2. 每个主任务中包含的N的子任务, 会再申请线程, 处理完毕释放回线程池
3. 启动了多个主任务时, 每个主任务在未结束之前, 都会占用自身一个线程不会释放, 消耗一个线程池资源
4. 后期频繁启动主任务, 可能使数量=线程池线程数, 此时子任务无法再从线程池获得资源, 就进入队列等待
5. 最终结果就造成了每个主任务都占用线程, 但主任务内的子任务无法获取线程, 线程池瘫痪不可用

问题复现

package test;import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;/*** @title 线程池异步线程中再次获取线程池资源的问题* @author Xingbz* @description*  记;**  究其原因在于:*      * @createDate 2020-7-17*/
@Slf4j
public class TestWork {private static final ThreadPoolTaskExecutor EXECUTOR;static {EXECUTOR = myExecutor();}/** 初始化线程池 */public static ThreadPoolTaskExecutor myExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 核心线程数executor.setCorePoolSize(5);// 最大线程数executor.setMaxPoolSize(20);// 排队任务队列executor.setQueueCapacity(100);// 线程名称前缀executor.setThreadNamePrefix("异步线程-");// 队列满后拒绝策略executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());// 线程最大回收时间executor.setKeepAliveSeconds(100);// 初始化线程executor.initialize();return executor;}/** 模拟测试 */public static void main(String[] args) throws Exception {// 主任务数量int mainJobNum = 20;CountDownLatch mainDownLatch = new CountDownLatch(mainJobNum);for (int i = 0; i < mainJobNum; i++) {// 主任务编号, 方便区分int index = i + 1;// 模拟每1秒开始一个主任务TimeUnit.SECONDS.sleep(1);EXECUTOR.submit(() -> {try {log.debug("\t执行主任务" + index);// 每个主任务随机包含N个子任务, 再异步调用线程池资源处理int subJobNum = RandomUtils.nextInt(2, 3);subJobWorkAsync(subJobNum, index);} finally {mainDownLatch.countDown();}});}mainDownLatch.await();EXECUTOR.shutdown();log.info("完成所有任务 > > >");}/** 异步执行子任务 */private static void subJobWorkAsync(int subJobNum, int index) {CountDownLatch subDownLatch = new CountDownLatch(subJobNum);for (int j = 0; j < subJobNum; j++) {EXECUTOR.submit(() -> {try {log.warn("\t\t\t执行一个" + index + "的子任务");// 每个子任务模拟耗时TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();} finally {subDownLatch.countDown();}});}try {subDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}}
}

执行代码, 结果如下:

可以看到, 线程池很快就被主任务耗尽, 导致子任务无法执行.

解决方案

1. 异步线程中不能再获取异步线程

既然主方法是异步执行了, 那么其中的子任务也相对不那么要求时间.此处是我为了业务给另外一个业务复用导致了线程再调线程

2. 如果异步中确实需要再获取异步线程, 需要使用新的线程池. 不能再使用自身的线程池

这是当前我们的解决方案, 在系统中又单独构建了一个线程池负责子任务的业务

线程池异步线程中再次获取线程池资源的问题相关推荐

  1. android 异步回调中操作UI线程,UI同步、卡死阻塞等性能问题

    android开发中,回调无处不在,整个android开发的框架就是以回调机制建立起来的.如:activity,service,broadcast,fragment,view事件监听,baseadap ...

  2. Java 线程实例二(终止线程、生产者/消费者问题、获取线程状态、获取所有线程、查看线程优先级、中断线程)

    终止线程 Java中原来在Thread中提供了stop()方法来终止线程,但这个方法是不安全的,所以一般不建议使用. 本文向大家介绍使用interrupt方法中断线程. 使用interrupt方法来终 ...

  3. java 线程执行结束_java中怎么判断线程执行完毕

    java中怎么判断线程执行完毕 发布时间:2020-05-15 15:18:05 来源:亿速云 阅读:316 作者:Leah java中怎么判断线程执行完毕?针对这个问题,今天小编总结这篇有关线程判断 ...

  4. ado.net mysql 连接池_ADO.NET中SQL Server数据库连接池

    实际上,大多数应用程序仅使用一个或几个不同的连接配置. 这意味着在执行应用程序期间,许多相同的连接将反复地打开和关闭. 为了使打开的连接成本最低,ADO.NET 使用称为连接池的优化方法. 连接池减少 ...

  5. java i线程安全吗_Java中 i++ 是线程安全的么?为什么?

    问题 在 int i = 0; i = i++; 语句中,i = i++是线程安全的么?如果不安全,请说明上面操作在JVM中的执行过程,为什么不安全?说出JDK中哪个类能达到以上的效果,并且是线程安全 ...

  6. java 动态增加线程,java - 在Java中动态停止线程 - SO中文参考 - www.soinside.com

    我需要一些有关线程的帮助,我需要制作一个可以动态关闭和打开线程的程序(这意味着当我需要一个线程时,它将运行,如果不再需要它,它将停止),现在我的问题是?如果我仅终止该线程的run方法,将其计为线程停止 ...

  7. [.Net线程处理系列]专题二:线程池中的工作者线程

    目录: 一.上节补充 二.CLR线程池基础 三.通过线程池的工作者线程实现异步 四.使用委托实现异步 五.任务 六.小结 一.上节补充 对于Thread类还有几个常用方法需要说明的. 1.1 Susp ...

  8. 详解线程池的作用及Java中如何使用线程池

    服务端应用程序(如数据库和 Web 服务器)需要处理来自客户端的高并发.耗时较短的请求任务,所以频繁的创建处理这些请求的所需要的线程就是一个非常消耗资源的操作.常规的方法是针对一个新的请求创建一个新线 ...

  9. 【Java 并发编程】线程池机制 ( 测试线程开销 | 启动线程分析 | 用户态 | 内核态 | 用户线程 | 内核线程 | 轻量级进程 )

    文章目录 一.测试线程开销 1.正常测试 2.不创建线程 3.只创建不启动线程 4.只启动不等待执行完成 二.分析测试结果 1.启动线程分析 2.用户线程与内核线程 3.轻量级进程 4.验证 Java ...

最新文章

  1. 摄像头光圈大小对景深的影响
  2. 我曾经得到的一个最好的编程建议
  3. iOS开发事件分发机制—响应链—手势影响
  4. 【IOI2018】会议【笛卡尔树】【dp】【线段树】
  5. 阿里P8亲自讲解!java实例变量和类变量
  6. ES6学习笔记(六)数组的扩展
  7. Poj 1338 Ugly Numbers(数学推导)
  8. json标注工具与labelme安装
  9. 决策树人工智能预测模型_部署和服务AI模型进行预测的10种方法
  10. JAVA集合系列(6):HashMap
  11. 音频分析工具:zplane de​​​​​​​Coda for Mac
  12. android 程序界面美化,Android ROM定制——界面美化基础(framework-res、SystemUI修改)...
  13. win7旗舰版安装vs2005
  14. 什么是数据结构?是举一个例子,叙述逻辑结构、存储结构和运算三个方面的内容。
  15. Thinkphp6快速入门教程
  16. 20、ZigBee 开发教程之基础篇—HC-SR501 人体红外传感器
  17. S3C22440 JTAG连接不上
  18. 《C语言入门》简单回文序列问题求解
  19. Win10如何使用win7的照片查看器
  20. geekbench5 cpu排名 202009

热门文章

  1. POJ:3461-Oulipo(KMP模板题)
  2. 将语音搜索集成到Google Now中
  3. duilib WindowImplBase BUG修复 --- 按一次ESC键, 关闭多个窗口
  4. [导入]如何动态生成table(javascript)
  5. 四种方式话Equal
  6. echarts柱状图x轴 label一行超过设置的字数换行
  7. session一些基本的东西
  8. nginx常见错误之(CreateFile() “D:\LCJ\下载\nginx/conf/nginx.conf“ failed (3: The system cannot find the path
  9. ES6学习笔记(五):轻松了解ES6的内置扩展对象
  10. 7-2 日期问题面向对象设计(聚合一) (35 分)