You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CountDownLatch计数未递减排查:嵌套ExecutorService场景

问题分析及解决方案

核心问题点

1. 自旋循环阻塞外层线程,后续代码无法执行

GrpcApiRunnable.run()中的while(System.currentTimeMillis()<serviceEndTime)是无限制的自旋循环,会持续占用外层线程池的线程,疯狂向内部线程池提交任务,直到duration时间耗尽才会执行后续的awaitTerminationAfterShutdown和countDownLatch.countDown()。如果duration设置较长,这个循环会直接卡住当前线程,导致CountDownLatch的计数迟迟无法递减。

同时,这种无控制的任务提交会瞬间填满内部线程池的任务队列,引发RejectedExecutionException,一旦异常未被捕获,会直接终止run()方法,countDownLatch.countDown()根本不会执行。

2. 未处理异常,countDown逻辑被跳过

awaitTerminationAfterShutdown方法通常包含executorService.shutdown()和executorService.awaitTermination(...),而awaitTermination会抛出InterruptedException。如果没有对该异常做捕获处理,一旦线程被中断,异常会向上抛出,直接终止run()方法,导致countDownLatch.countDown()无法执行,这是CountDownLatch计数未递减的最常见原因。

3. 任务提交逻辑不合理

自旋提交任务的方式完全没有控制速率,不仅会压垮内部线程池,还会浪费外层线程的资源。


修复方案

1. 重构任务提交逻辑,避免自旋阻塞

用单独线程负责提交任务,释放外层线程的同时控制提交速率,避免任务队列爆炸:

@Override
public void run() {
    ExecutorService executorService = Executors.newFixedThreadPool(noOfThreads);
    long serviceEndTime = System.currentTimeMillis() + duration;

    // 单独线程处理任务提交,不阻塞外层线程
    Thread submitThread = new Thread(() -> {
        while (System.currentTimeMillis() < serviceEndTime) {
            try {
                executorService.submit(() -> {
                    // do something
                });
                // 控制提交速率,按需调整间隔
                Thread.sleep(10);
            } catch (RejectedExecutionException e) {
                // 处理线程池拒绝异常
                e.printStackTrace();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
        executorService.shutdown(); // 提交完成后关闭线程池
    });
    submitThread.start();

    try {
        // 等待内部线程池所有任务执行完成
        if (!executorService.awaitTermination(duration + 1000, TimeUnit.MILLISECONDS)) {
            executorService.shutdownNow(); // 超时强制关闭
        }
        submitThread.join(); // 等待提交线程结束
    } catch (InterruptedException e) {
        executorService.shutdownNow();
        submitThread.interrupt();
        Thread.currentThread().interrupt();
    } finally {
        // 无论是否异常,确保countDown执行
        countDownLatch.countDown();
    }
}

2. 外层线程池使用后关闭

在performanceTestV21方法中,提交完所有任务后关闭外层线程池,避免资源泄漏:

public JsonObject performanceTestV21(long duration, int noOfThreads) throws InterruptedException {
    int testCount = getGrpcTestInfos().size();
    ExecutorService outerExecutorService = Executors.newFixedThreadPool(testCount);
    CountDownLatch countDownLatch = new CountDownLatch(testCount);
    
    for (GrpcTestInfo grpcTestInfo : getGrpcTestInfos()) {
        outerExecutorService.submit(new GrpcApiRunnable(noOfThreads, duration, countDownLatch));
    }

    outerExecutorService.shutdown(); // 关闭外层线程池,不再接受新任务
    countDownLatch.await();
    
    // 可选:等待外层线程池剩余任务完成
    if (!outerExecutorService.awaitTermination(duration + 2000, TimeUnit.MILLISECONDS)) {
        outerExecutorService.shutdownNow();
    }
    
    return new JsonObject(performanceMatrix);
}

内容的提问来源于stack exchange,提问作者Arindam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 14:03:11