通过Java ProcessBuilder运行可执行文件时进程挂起问题
解决ProcessBuilder调用exe进程挂起的问题
这种情况我太熟悉了!你碰到的是Java ProcessBuilder调用外部进程时非常典型的缓冲区阻塞问题——为啥CMD里跑没问题?因为Windows的命令提示符会自动实时消费子进程的标准输出和错误输出,但Java默认不会帮你做这件事。
问题根源
当main.exe输出内容(包括标准输出和错误输出)时,这些内容会先存到系统缓冲区里(一般是4KB或8KB大小)。如果Java程序不主动读取这些缓冲区,一旦缓冲区被填满,main.exe就会暂停执行,等待缓冲区有空闲空间;而你的Java程序又在调用waitFor()等待main.exe结束,两边互相等,就形成了死锁,最后一行输出自然打不出来,进程也挂起了。
解决方案
下面给你几种实用的解决办法,根据你的需求选就行:
1. 用线程分别读取标准输出和错误输出(最通用)
适合需要区分正常输出和错误输出,或者要实时打印输出的场景:
public static void main(String[] args) throws IOException, InterruptedException { ProcessBuilder builder = new ProcessBuilder("main.exe", "C:\\Users\\ozdav\\Desktop\\your-file-path"); Process process = builder.start(); // 启动线程读取标准输出 new Thread(() -> { try (BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { String line; while ((line = stdoutReader.readLine()) != null) { System.out.println("Exe正常输出: " + line); } } catch (IOException e) { e.printStackTrace(); } }).start(); // 启动线程读取错误输出 new Thread(() -> { try (BufferedReader stderrReader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) { String line; while ((line = stderrReader.readLine()) != null) { System.err.println("Exe错误输出: " + line); } } catch (IOException e) { e.printStackTrace(); } }).start(); // 等待进程执行完成 int exitCode = process.waitFor(); System.out.println("进程退出码: " + exitCode); }
2. 合并错误输出到标准输出(简化代码)
如果不需要区分正常输出和错误输出,可以让ProcessBuilder把错误流合并到标准流,这样只需要读一个流:
public static void main(String[] args) throws IOException, InterruptedException { ProcessBuilder builder = new ProcessBuilder("main.exe", "C:\\Users\\ozdav\\Desktop\\your-file-path"); // 开启错误流合并到标准输出 builder.redirectErrorStream(true); Process process = builder.start(); // 读取合并后的输出 try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { System.out.println("Exe输出: " + line); } } int exitCode = process.waitFor(); System.out.println("进程退出码: " + exitCode); }
3. 重定向输出到文件(适合大输出场景)
如果exe输出内容很多,不需要实时打印,直接把输出写到文件里更高效:
public static void main(String[] args) throws IOException, InterruptedException { ProcessBuilder builder = new ProcessBuilder("main.exe", "C:\\Users\\ozdav\\Desktop\\your-file-path"); // 重定向标准输出到指定文件 builder.redirectOutput(new File("exe_output.txt")); // 重定向错误输出到指定文件 builder.redirectError(new File("exe_error.txt")); Process process = builder.start(); int exitCode = process.waitFor(); System.out.println("进程退出码: " + exitCode); }
额外注意点
- 一定要在调用
waitFor()之前启动读取流的操作,不然还是会触发阻塞。 - 如果你的
main.exe最后一行输出没有换行符,readLine()会一直等待换行,这种情况可以换成逐字节读取,或者用InputStream.available()判断是否还有内容。 - 如果exe需要输入(比如等待用户输入),还要记得处理
process.getOutputStream(),不过你CMD里能正常运行,大概率不是输入的问题。
内容的提问来源于stack exchange,提问作者David Oz
相关产品推荐
相关产品推荐

