首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java Runtime.exec() 坑點深度解析:阻塞、死鎖、命令解析與最佳實踐

     編輯:Java 綜合教學

Runtime.exec() 為什麼會「卡死」?

許多開發者在 Windows 下調用 Runtime.exec("query session") 或 cmd.exe /c dir 時遇到 waitFor() 永不返回、readLine() 阻塞、或拋出 IllegalThreadStateException。根因在於 子進程輸出/錯誤流未及時消費導致緩衝區填滿,引發死鎖。

四大經典坑點

坑點現象根因解法
1. 直接調用 exitValue()IllegalThreadStateException進程未結束就取退出碼改用 waitFor() 或捕獲異常
2. 忽略輸出/錯誤流waitFor() 掛起、readLine() 阻塞子進程寫滿管道緩衝區啟動獨立線程消費 InputStream/ErrorStream
3. 將內部命令當可執行程序IOException: CreateProcess error=2dir、copy 等是 cmd 內建命令用 cmd.exe /c "command" 包裹
4. 當 Shell 用(管道、重定向)重定向失效、參數被錯誤分割exec() 不解析 Shell 語法在 Java 代碼中手動處理流重定向/管道

正確範本:StreamGobbler 異步消費流

class StreamGobbler extends Thread {
    private final InputStream is;
    private final String type;
    private final OutputStream redirect; // 可選:重定向到文件

    StreamGobbler(InputStream is, String type) { this(is, type, null); }
    StreamGobbler(InputStream is, String type, OutputStream redirect) {
        this.is = is; this.type = type; this.redirect = redirect;
    }

    @Override public void run() {
        try (BufferedReader br = new BufferedReader(new InputStreamReader(is, "GBK"));
             PrintWriter pw = redirect != null ? new PrintWriter(redirect) : null) {
            String line;
            while ((line = br.readLine()) != null) {
                if (pw != null) pw.println(line);
                System.out.println(type + "> " + line);
            }
            if (pw != null) pw.flush();
        } catch (IOException e) { e.printStackTrace(); }
    }
}

完整調用示例(Windows)

public static void execWin(String cmd) throws Exception {
    String[] cmds = { "cmd.exe", "/C", cmd };
    Process proc = Runtime.getRuntime().exec(cmds);

    new StreamGobbler(proc.getErrorStream(), "ERROR").start();
    new StreamGobbler(proc.getInputStream(), "OUTPUT").start();

    int exit = proc.waitFor();
    System.out.println("ExitValue: " + exit);
}

進階建議

  • 優先用 ProcessBuilder:支持目錄、環境變量、重定向輸出到文件(pb.redirectOutput(file)),API 更現代。
  • 編碼指定:Windows 控制台預設 GBK,Linux UTF-8,構建 InputStreamReader 時顯式指定。
  • 超時控制:Java 9+ proc.waitFor(timeout, TimeUnit) 避免無限等待。
  • 避免大量輸出:若子進程輸出極大,考慮臨時文件或分頁讀取。

排查清單

  1. 命令在 CMD 中能單獨跑通?
  2. 是否用 cmd.exe /c 包裹內部命令?
  3. 是否同時啟動兩個 Gobbler 線程消費 stdout/stderr?
  4. 編碼是否匹配?
  5. 是否在 waitFor() 前就已消費完流?

掌握以上原則,Runtime.exec() / ProcessBuilder 就不再是「坑」,而是可控的外部進程調用利器。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved