| 特性 | Runtime.exec() | ProcessBuilder |
|---|---|---|
| API 風格 | 單一方法多重載 | 建構者模式、鏈式配置 |
| 參數處理 | 字串自動分割(跨平台易出錯) | List<String> 明確指定參數 |
| 環境變數 | 難以自訂 | environment() Map 直接操作 |
| 工作目錄 | 依賴系統屬性 | directory(File) 明確設定 |
| 錯誤流合併 | 不支援 | redirectErrorStream(true) |
| 推薦場景 | 簡單指令、舊版相容 | 複雜參數、生產環境、跨平台 |
ProcessBuilder pb = new ProcessBuilder("java", "-version");
// 或帶參數:new ProcessBuilder("java", "-cp", "app.jar", "com.Main")
// 1. 設定環境變數
Map env = pb.environment();
env.put("CUSTOM_VAR", "value1");
env.remove("UNWANTED_VAR");
// 2. 設定工作目錄
pb.directory(new File("/opt/myapp"));
// 3. 合併錯誤流到標準輸出(避免雙流阻塞死鎖)
pb.redirectErrorStream(true);
// 4. 重導向輸出到檔案(JDK 7+)
pb.redirectOutput(new File("app.log"));
pb.redirectError(new File("app.err"));
// 5. 啟動行程
Process process = pb.start();
子行程的標準輸出/錯誤輸出緩衝區有限,若不及時讀取會導致子行程阻塞。必須使用獨立執行緒同時消費兩個流:
Process process = pb.start();
// 執行緒 1:讀取標準輸出
Thread outputThread = new Thread(() -> {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println("[OUT] " + line);
}
} catch (IOException e) { /* 處理 */ }
});
// 執行緒 2:讀取錯誤輸出(若未 redirectErrorStream)
Thread errorThread = new Thread(() -> {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getErrorStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
System.err.println("[ERR] " + line);
}
} catch (IOException e) { /* 處理 */ }
});
outputThread.start();
errorThread.start();
// 等待行程結束
int exitCode = process.waitFor();
outputThread.join();
errorThread.join();
try (OutputStream stdin = process.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin))) {
writer.write("輸入資料\n");
writer.flush(); // 必須 flush 才會送達
}
ProcessHandle:取得 PID、啟動時間、父行程、子行程樹、CPU/記憶體使用率ProcessHandle.onExit():回傳 CompletableFuture<ProcessHandle> 非同步等待結束ProcessBuilder.startPipeline(List<ProcessBuilder>):管線串連多個行程(類似 Shell |)ProcessHandle 監控資源,設定超時機制StandardCharsets.UTF_8 或系統預設編碼waitFor() 或 destroyForcibly() 回收資源