Java進程調用外部程序的幾種方法


Java進程調用外部程序的幾種方法

掃地生在之前有記錄通過Java程序控制遠程服務器進而調用指定程序的筆記。使用java代碼連接到局域網內的Windows服務器中的軟件並執行指令

現在在簡單總結一下Java進程調用外部程序的幾種方法,期間會簡單的通過源碼來看一下。

通過Java執行系統命令,與cmd中或Linux終端上一種執行Shell命令,最典型的用法就是使用Runtime,getRuntime().exec(command)或者new ProcessBuilder(cmdArray).start().

1 Runtime

Runtime類是Java程序的運行時環境。不能new出一個Runtime對象,只能通過getRuntime()方法獲取當前Runtime運行時對象的引用。然后可以調用Runtime的方法查看和修改Java虛擬機的狀態。

image-20220109213437020

通過上圖可以看出,Runtime類並沒有提供構造函數給我們,所以說我們是無法new一個Runtime對象的。這是簡單單例,可以看到它的getRuntime()方法是沒有做並行處理的,但整個環境中只允許出現一個Runtime對象。

Runtime和ProcessBuilder的不同點就是,啟動子進程時的命令形式不同,Runtime.getRuntime.exec()可以把命令和參數寫在一個String中,用空格分開,ProcessBuilder則是構造函數的參數中,傳遞一個由命令和參數組成的list或數組

Runtime類主要的方法如下:
image-20220109213733664

image-20220109213829693

  1. exec方法接收一個命令然后執行,通過該方法可以執行和cmd中用法一樣命令。比如,Runtime.getRuntime().exec(“ls”),就和cmd中執行ls效果一樣了。
  2. freeMemory()可以查看當前虛擬機內存中空閑內存還有多少。
  3. totalMemory()可以查看當前虛擬機使用的總內存大小。
  4. maxMemory()可以查看JVM的最終可以使用的最大內存是多少。
  5. availableProcessors()可以查看本機有多少處理器,即本機處理器是多少核。
  6. exit(int)方法可以退出當前Java程序的運行,System.exit(int)方法就是調用了Runtime.exit(int)方法來退出運行的。

以上這些在我們做分布式並行化編程查看計算機參數時也會使用到。

@Test
public void test2() throws IOException {
    Runtime.getRuntime().exec("java -version");
    System.out.println(Runtime.getRuntime().freeMemory());
    System.out.println(Runtime.getRuntime().totalMemory());
    System.out.println(Runtime.getRuntime().maxMemory());
}

image-20220109215651974

2 Process

Process是一個抽象類,主要方法如下:

image-20220109214513047

  1. waitFor()是讓當前主進程等待這個process指向的子進程執行完成。
  2. exitValue()可以查看process指向的子進程執行完的退出值,0代表是正常運行結束。
  3. destroy()和destroyForcibly()可以終止process子進程的運行,后者是強制終止,前者與平台終止進程的具體實現有關。

通過Runtime.getRuntime().exe(...)可以創建一個本地進程執行傳入的命令,這個方法返回Process的一個實例:

image-20220109214717818

process指向一個本地進程,相對於main進程來說,process指向的稱為子進程。其中的is是為了獲取子進程的輸出信息。

明明是獲取輸出信息,為什么是InputStream呢?因為相對於main進程來說,子進程的輸出就是main進程的輸入,所以是InputStream。vice verse,如果要向子進程傳遞參數或者輸入信息,則應該用OutputStream。但是不推薦用java 1.0引入的Process,而是用java 5.0的ProcessBuilder替代。

3 ProcessBuilder

ProcessBuilder是java 5.0引入的,start()方法返回Process的一個實例,如:

private static boolean processMp4(String oldfilepath) {
    if (!checkfile(inputPath)) {
        log.error(oldfilepath + "不是文件路徑");
        return false;
    }
    List<String> command = new ArrayList<String>();
    command.add("ffmpeg");
    command.add("-i");
    command.add(oldfilepath);
    command.add("-c:v");
    command.add("libx264");
    command.add("-mbd");
    command.add("0");
    command.add("-c:a");
    command.add("aac");
    command.add("-strict");
    command.add("-2");
    command.add("-pix_fmt");
    command.add("yuv420p");
    command.add("-movflags");
    command.add("faststart");
    command.add(outputPath + "a.mp4");
    try {
        Process videoProcess = new ProcessBuilder(command).redirectErrorStream(true).start();
        new PrintStream(videoProcess.getErrorStream()).start();
        new PrintStream(videoProcess.getInputStream()).start();
        videoProcess.waitFor();
        return true;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

創建ProcessBuilder不需要通過Runtime,而Runtime.getRimtime().exec(string)正是調用了ProcessBuilder的構造方法來創建子進程並執行的。

ProcessBuilder的構造方法接收一個命令參數的數組形式,其中,第一個元素代表要執行的系統命令,后面的元素代表要傳給該命令的參數

調用.start()方法運行之后,就可以獲得該子進程的Process引用了,然后就可以調用Process的方法進行處理。

在用Runtime.getRuntime().exec()或ProcessBuilder(array).start()創建子進程Process之后,一定要及時取走子進程的輸出信息和錯誤信息,否則輸出信息流和錯誤信息流很可能因為信息太多導致被填滿,最終導致子進程阻塞住,然后執行不下去。

比如上面掃地生的代碼中使用線程及時取走了輸出信息和錯誤信息:

new PrintStream(videoProcess.getErrorStream()).start();
/**
 * 在用Runtime.getRuntime().exec()或ProcessBuilder(array).start()創建子進程Process之后,
 * 一定要及時取走子進程的輸出信息和錯誤信息,否則輸出信息流和錯誤信息流很可能因為信息太多導致被填滿,
 * 最終導致子進程阻塞住,然后執行不下去。
 */
class PrintStream extends Thread {
    InputStream is = null;
    public PrintStream(InputStream is)
    {
        this.is = is;
    }
    @Override
    public void run() {
        try{
            while(true) {
                int ch = is.read();
                if(ch != -1) {
                    System.out.print((char)ch);
                } else {
                    break;
                }
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM