You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Java问题:关闭OutputStream前无法读取InputStream

关于ProcessBuilder中InputStream与OutputStream的读写冲突问题

首先明确:你的代码里的InputStream和OutputStream本身并没有冲突,问题出在你的单线程读写逻辑设计上

问题根源分析

  1. 冗余的错误流读取:你已经设置了processBuilder.redirectErrorStream(true),这会把Process的错误输出合并到标准输入流中,所以后面的bufferedReaderError根本读不到任何内容,这部分代码完全是多余的。
  2. 单线程阻塞导致的死锁:你的核心逻辑是在一个线程里先进入readLine()的阻塞循环——readLine()会一直等待Process的输出,直到对方关闭输出流才会返回null。你在循环里只写了一次"hello"并flush,但:
    • 你没有给命令加换行符(\n),像jdb这类命令行工具需要接收到换行才会执行你输入的命令,所以它可能还在等待你输入回车;
    • 写完命令后,你的线程立刻又回到readLine()阻塞状态,而jdb可能在处理完命令后会输出内容,但因为你的线程一直卡在读取上,没有后续的交互处理,加上之前的命令没触发执行,就导致了看起来必须关闭Writer才能继续的假象——关闭Writer相当于给jdb发送了EOF信号,jdb处理完后退出,关闭输出流,readLine()才会返回null,循环结束。

修正后的解决方案

要解决这个问题,你需要把读取Process输出向Process写入命令拆分成两个独立的线程,避免互相阻塞。同时要注意给命令加上换行符,触发命令行工具执行。

以下是调整后的代码示例:

Thread thread = new Thread() {
    public void run() {
        try {
            // 注意jdb和path之间加空格,避免命令拼接错误
            ProcessBuilder processBuilder = new ProcessBuilder("bash", "-c", "jdb " + path);
            processBuilder.redirectErrorStream(true);
            Process process = processBuilder.start();

            // 单独开线程处理Process的输出(读取到控制台)
            Thread outputReader = new Thread(() -> {
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        consoleWindow.appendText(line);
                        // 可以根据jdb的提示符(比如">")来自动发送命令
                        if (line.trim().endsWith(">")) {
                            sendCommandToProcess(process, "hello");
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            outputReader.start();

            // 等待Process执行完成,同时等待输出线程结束
            int exitCode = process.waitFor();
            outputReader.join();
            consoleWindow.appendText("\nProcess exited with code: " + exitCode);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 封装向Process写入命令的方法
    private void sendCommandToProcess(Process process, String command) {
        try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()))) {
            writer.write(command);
            writer.newLine(); // 必须加换行,触发命令执行
            writer.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
};
thread.start();

关键优化点

  • 拆分读写线程:读取输出的线程独立运行,不会阻塞写入操作,反之亦然;
  • 添加命令换行:通过writer.newLine()给命令加上回车,确保jdb能识别并执行你的输入;
  • 移除冗余的错误流读取:利用redirectErrorStream(true)的特性,只需要处理一个输入流即可;
  • 基于输出触发写入:根据jdb的提示符判断时机发送命令,模拟真实的命令行交互逻辑。

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

火山引擎 最新活动