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

ExecutorService如何非阻塞运行 解决HTTP请求阻塞问题

问题根源

你的代码出现阻塞的核心原因是异步逻辑完全写反了:

  • 你虽然用了ExecutorService提交任务,但转头就通过主线程Handler把最重的Jsoup解析逻辑processSourceCode()全部post回了Android主线程执行。
  • Android主线程是单线程串行处理所有消息的,耗时的Jsoup解析占住主线程时,后续的HTTP请求发起逻辑、其他网络回调都会被堵在消息队列里,必须等前一个解析任务跑完才能执行,和你观察到的「停用响应处理就没有阻塞」的现象完全一致。
  • 你配置的100线程的线程池完全没有承担任何耗时计算工作,只是做了一次无意义的线程跳转。
修复步骤
  • 把耗时逻辑全部留在子线程执行,只有UI操作才切主线程。不要把整个解析任务post回主线程,Jsoup解析属于CPU密集型耗时操作,直接放在线程池的工作线程执行即可,解析完成后如果需要更新页面,再把轻量的UI操作切回主线程。
  • 优化线程池配置。不要每个MakeRequest实例都新建一个核心数100的线程池,线程是重量级系统资源,无限制创建会导致频繁的CPU调度、内存占用过高,全局共用一个适配当前设备核心数的线程池即可。
  • 确认网络回调线程配置。如果你用的网络客户端默认把响应回调派发到主线程(比如OkHttp默认Callback、HttpURLConnection的常见封装),只要主线程没有被耗时任务占住,直接在回调里发起下一个请求不会有阻塞问题。
修正后代码参考

MakeRequest.java

class MakeRequest{
    // 全局共用线程池,按CPU核心数配置合理的线程数,IO+计算混合任务取核心数*2即可
    public static final ExecutorService executor = Executors.newFixedThreadPool(
            Runtime.getRuntime().availableProcessors() * 2
    );
    private final Handler mainHandler = new Handler(Looper.getMainLooper());

    public void sentDataToJsoup(final String html, final String sentUrl) {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                // 耗时的Jsoup解析直接在子线程执行,不占用主线程
                MyJsoup jsoupProcessor = new MyJsoup(html, sentUrl);
                // processSourceCode返回解析后的结构化结果
                final Object parseResult = jsoupProcessor.processSourceCode();

                // 仅当需要更新UI、操作主线程组件时,才切回主线程执行轻量操作
                mainHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        // 此处仅写UI更新逻辑,比如刷新列表、展示结果,禁止放耗时任务
                    }
                });
            }
        });
    }

    // 页面销毁时调用,避免线程池泄漏
    public static void release(){
        executor.shutdown();
    }
}

MainPage.java 逻辑说明

修正后sentDataToJsoup方法提交异步任务后会立刻返回,不会等待解析完成,因此processHttpResponse里的执行顺序是:

  • 提交请求1的解析任务到线程池(立刻返回,不阻塞)
  • 立刻执行request2.makeSecondRequest()发起第二个请求
  • 线程池中空闲的工作线程会自动处理请求1的响应解析,完全不阻塞后续请求的发送和接收
额外注意事项
  • 不要在主线程执行任何超过100ms的操作,包括Jsoup解析、文件读写、数据库操作,所有耗时逻辑全部下沉到工作线程。
  • 如果需要在解析完成后触发后续请求,把请求发起逻辑放在解析任务的子线程部分即可,不用切主线程。
  • 如果需要处理并发请求的响应顺序问题,可以给每个请求加唯一标识,避免响应结果错位。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 11:03:24