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

Java 多行程程式設計:ProcessBuilder 與 Runtime.exec 完整實戰

     編輯:Java 相關知識

Java 啟動外部行程兩大 API 比較

特性Runtime.exec()ProcessBuilder
API 風格單一方法多重載建構者模式、鏈式配置
參數處理字串自動分割(跨平台易出錯)List<String> 明確指定參數
環境變數難以自訂environment() Map 直接操作
工作目錄依賴系統屬性directory(File) 明確設定
錯誤流合併不支援redirectErrorStream(true)
推薦場景簡單指令、舊版相容複雜參數、生產環境、跨平台

ProcessBuilder 完整配置範例


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();

行程 I/O 處理:避免阻塞死鎖的關鍵

子行程的標準輸出/錯誤輸出緩衝區有限,若不及時讀取會導致子行程阻塞。必須使用獨立執行緒同時消費兩個流:


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 才會送達
}

Java 9+ 新增功能

  • ProcessHandle:取得 PID、啟動時間、父行程、子行程樹、CPU/記憶體使用率
  • ProcessHandle.onExit():回傳 CompletableFuture<ProcessHandle> 非同步等待結束
  • ProcessBuilder.startPipeline(List<ProcessBuilder>):管線串連多個行程(類似 Shell |)

常見坑點與解決方案

  1. Windows 路徑空白:參數列表模式自動處理,勿手動加引號
  2. 長時間運行行程:使用 ProcessHandle 監控資源,設定超時機制
  3. 編碼亂碼:明確指定 StandardCharsets.UTF_8 或系統預設編碼
  4. 僵屍行程:必須呼叫 waitFor() 或 destroyForcibly() 回收資源
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved