当前位置: 代码网 > it编程>编程语言>Java > 关于线程池异步线程中再次获取线程池资源的问题

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

2024年08月20日 Java 我要评论
问题描述在线上发生的一次问题,在场景中有这样一个业务,需要异步执行一个主任务,主任务中又包含着n个子任务,为了整个主任务能够快速处理,又将子任务按照数量获取线程资源异步处理,即异步线程a中再异步调用a

问题描述

在线上发生的一次问题,在场景中有这样一个业务,需要异步执行一个主任务,主任务中又包含着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. 如果异步中确实需要再获取异步线程,需要使用新的线程池. 不能再使用自身的线程池

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

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com