| 特性 | Runtime.exec() | ProcessBuilder |
| API 風格 | 靜態方法、多載豐富 | Builder 模式、鏈式設定 |
| 環境變數控制 | 陣列傳遞,較死板 | environment() 回傳 Map 可動態修改 |
| 工作目錄 | 參數傳遞 | directory(File) |
| 錯誤流合併 | 不支援 | redirectErrorStream(true) |
| 推薦度 | 舊版相容 | JDK 5+ 首選 |
核心類別:Process
啟動後回傳 Process 實例,提供:
getInputStream():讀取標準輸出
getErrorStream():讀取錯誤輸出
getOutputStream():寫入標準輸入
waitFor():阻塞等待結束,回傳結束碼
exitValue():非阻塞取得結束碼(未結束拋異常)
destroy():嘗試終止子進程(不保證成功)
完整範例:ProcessBuilder + 流合併 + 執行緒讀取
public class ExternalProcessRunner {
public static void main(String[] args) throws Exception {
List cmd = Arrays.asList("cmd.exe", "/c", "dir");
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.directory(new File("C:\\"));
pb.redirectErrorStream(true); // 關鍵:合併錯誤流到標準輸出
Process p = pb.start();
// 非阻塞讀取輸出
new Thread(() -> {
try (BufferedReader br = new BufferedReader(
new InputStreamReader(p.getInputStream(), "GBK"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println("[OUT] " + line);
}
} catch (IOException e) { e.printStackTrace(); }
}).start();
int exitCode = p.waitFor();
System.out.println("Process exited with code: " + exitCode);
}
}
阻塞成因與三大解法
| 成因 | 現象 | 解法 |
| 只讀錯誤流,標準輸出緩衝區滿 | 進程卡住 | 先讀標準輸出,再讀錯誤流;或合併流 |
| 大量輸出未及時讀取 | 緩衝區滿導致死鎖 | 獨立執行緒持續讀取兩條流 |
先呼叫 waitFor() 再讀流 | 進程未結束因緩衝區滿,主執行緒永久等待 | 先讀流,最後呼叫 waitFor() |
最佳實務模板
ProcessBuilder pb = new ProcessBuilder(commandList)
.directory(workDir)
.redirectErrorStream(true); // 方案一:合併流
Process p = pb.start();
// 方案二:雙執行緒讀取(不合併流時)
StreamGobbler outGobbler = new StreamGobbler(p.getInputStream(), "OUT");
StreamGobbler errGobbler = new StreamGobbler(p.getErrorStream(), "ERR");
outGobbler.start(); errGobbler.start();
int exitCode = p.waitFor(); // 最後等待
在 Java 中啟動另一個 Java 程式
String javaExe = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
String classpath = System.getProperty("java.class.path");
List cmd = new ArrayList<>();
cmd.add(javaExe);
cmd.add("-classpath");
cmd.add(classpath);
cmd.add("com.example.MyClass");
cmd.add("arg1", "arg2");
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.redirectErrorStream(true);
Process p = pb.start();
// ... 讀取輸出、waitFor()
關鍵提醒
- 僅能啟動實際可執行檔,Shell 內建指令(如
cd、set)需透過 cmd.exe /c 或 bash -c
- 編碼一致性:讀取流時指定正確 Charset(Windows 常用 GBK/CP950,Linux 用 UTF-8)
- 超時控制:Java 9+ 可用
p.waitFor(timeout, TimeUnit.SECONDS)
- 資源釋放:
try-with-resources 確保流關閉
掌握 ProcessBuilder 與流控制模式,能讓 Java 應用可靠地整合外部工具、腳本與舊系統。
- 上一篇文章: JavaFX 與 Java 互操作性現狀與解決方案:反射、介面、ScriptEngine 三大路徑
- 下一篇文章: Java 與 Ruby on Rails 整合對比:以 OpenXava 為例的模型驅動開發優勢分析
Copyright ©
程式碼之家 All Rights Reserved