1. 程式人生 > >Runtime.getRuntime().exec() 輸出流阻塞問題

Runtime.getRuntime().exec() 輸出流阻塞問題

有時候我們可能需要呼叫系統外部的某個程式,此時就可以用Runtime.getRuntime().exec()來呼叫,他會生成一個新的程序去執行呼叫的程式。
此方法返回一個java.lang.Process物件,該物件可以得到之前開啟的程序的執行結果,還可以操作程序的輸入輸出流。

Process物件有以下幾個方法:
  1、destroy()      殺死這個子程序
  2、exitValue()      得到程序執行結束後的返回狀態
  3、waitFor()       得到程序執行結束後的返回狀態,如果程序未執行完畢則等待知道執行完畢
  4、getInputStream()  得到程序的標準輸出資訊流
  5、getErrorStream()  得到程序的錯誤輸出資訊流
  6、getOutputStream() 得到程序的輸入流

現在來講講exitValue(),當執行緒沒有執行完畢時呼叫此方法會跑出IllegalThreadStateException異常,最直接的解決方法就是用waitFor()方法代替。

但是waitFor()方法也有很明顯的弊端,因為java程式給程序的輸出流分配的緩衝區是很小的,有時候當程序輸出資訊很大的時候回導致緩衝區被填滿,如果不及時處理程式會阻塞。如果程式沒有對程序的輸出流處理的會就會導致執行exec()的執行緒永遠阻塞,程序也不會執行下去直到輸出流被處理或者java程式結束。
解決的方法就是處理緩衝區中的資訊,開兩個執行緒分別去處理標準輸出流和錯誤輸出流。

public class
ExecTest { public static void main(String[] args) throws IOException, InterruptedException {   String cmd = "cmd /c dir D:\\";   final Process process = Runtime.getRuntime().exec(cmd);   printMessage(process.getInputStream());   printMessage(process.getErrorStream());   int value
= process.waitFor();   System.out.println(value); } private static void printMessage(final InputStream input) {   new Thread(new Runnable() {   public void run() {     BufferedReader bf = new BufferedReader(new InputStreamReader(input));     String line = null;     try {     while((line=bf.readLine())!=null) {     System.out.println(line);     }     } catch (IOException e) {     e.printStackTrace();     }  }   }).start(); } }

擴充套件-1 可以選擇將子程序中的輸出流重定向到一個檔案中。

//這種方式有文章說不是很推薦,原因是Runtime.exec()不能很好的執行復雜命令列 
// > D:\\1.txt 2>&1表示錯誤輸出和標準輸出均重定向到1.txt檔案
public class ExecTest {

    public static void main(String[] args) throws IOException, InterruptedException {
      String cmd = "cmd /c dir D:\\ > D:\\1.txt 2>&1";
      final Process process = Runtime.getRuntime().exec(cmd);
      
      int value = process.waitFor();
      System.out.println(value);
    }   
}

擴充套件-2 將父程序的標準輸出和錯誤輸出重定向

public class ExecTest {

    public static void main(String[] args) throws IOException, InterruptedException {
       PrintStream ps = new PrintStream("D:\\log.txt");
       System.setErr(ps);
       System.setOut(ps);
      String[] cmdLine = new String[]{"python","D:/test.py","are","you","ok?"};
      final Process process = Runtime.getRuntime().exec(cmdLine);
      printMessage(process.getInputStream());
      printMessage(process.getErrorStream());
      int value = process.waitFor();
      System.out.println(value);
    }

    private static void printMessage(final InputStream input) {
      new Thread(new Runnable() {
         public void run() {
            BufferedReader bf = new BufferedReader(new InputStreamReader(input));
            String line = null;
             try {
                while((line=bf.readLine())!=null) {
                    System.out.println(line);
                }
             } catch (IOException e) {
                e.printStackTrace();
             }
         }
      }).start();
    }
}

test.py

import sys
import pandas as pd

for i in range(1,len(sys.argv)):
    print sys.argv[i]

  • 子程序的標準輸出和錯誤輸出轉移到父程序中的標準輸出,
  • 父程序的標準輸出和錯誤輸出重定向到檔案log.txt 中。
  • 因此 所有輸出結果可在log.txt 中檢視。

參考文獻: