Java问题:关闭OutputStream前无法读取InputStream
关于ProcessBuilder中InputStream与OutputStream的读写冲突问题
首先明确:你的代码里的InputStream和OutputStream本身并没有冲突,问题出在你的单线程读写逻辑设计上。
问题根源分析
- 冗余的错误流读取:你已经设置了
processBuilder.redirectErrorStream(true),这会把Process的错误输出合并到标准输入流中,所以后面的bufferedReaderError根本读不到任何内容,这部分代码完全是多余的。 - 单线程阻塞导致的死锁:你的核心逻辑是在一个线程里先进入
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




