Java調用外部程序(如Windows下的Exe,或Linux的Shell)

序言

         java程序可以調用Jvm外部的一些應用程序來完成一些功能.目前基於JDK1.8來做實驗~

        目前java有如下兩種方式來調用其它程序, Runtime和ProcessBulider提供了不同的方式來啓動程序,設置啓動參數環境變量工作目錄。(這些都能設置,就是幫你找到外部可執行文件的位置與啓動變量,具體怎麼執行,可以查看Runtime和ProcessBuilder的構造方法,裏面有設置)

       但是這兩種方法都會返回一個用於管理操作系統進程的Process對象。另外兩者的啓動差別不是很大.最終生成的Process子進程是獨立於Jvm之外的進程,就算沒有對象應用指向它,它也不會被GC關閉或者釋放.

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

       這麼來說其實重要的是Process的使用.

  1. Runtime的exec()方法,並返回一個Process對象
  2. ProcessBuilder的start()方法,並返回一個Process對象

Runtime

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

        Runtime的主要的方法如下所示:

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

 

 

ProcessBuilder

        ProcessBuilder是java 5.0引入的,start()方法返回Process的一個實例.所以被推薦使用~~

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

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

        ProcessBuilder 沒有Runtime那樣直接獲取環境參數的方法,只有一個getenviroment返回一個Map~.

     

Process

看下Api怎麼說的,從中分理處一些關鍵信息;

java.lang 
類 Process
java.lang.Object

java.lang.Process

public abstract class Processextends Object

ProcessBuilder.start() 和 Runtime.exec 方法創建一個本機進程,並返回 Process 子類的一個實例,該
實例可用來控制進程並獲得相關信息。Process 類提供了執行從進程輸入、執行輸出到進程、等待進程完成、檢
查進程的退出狀態以及銷燬(殺掉)進程的方法。 
創建進程的方法可能無法針對某些本機平臺上的特定進程很好地工作,比如,本機窗口進程,守護進程,
Microsoft Windows 上的 Win16/DOS 進程,或者 shell 腳本。創建的子進程沒有自己的終端或控制檯。它的
所有標準 io(即 stdin、stdout 和 stderr)操作都將通過三個流 (getOutputStream()、
getInputStream() 和 getErrorStream()) 重定向到父進程。父進程使用這些流來提供到子進程的輸入和獲得
從子進程的輸出。因爲有些本機平臺僅針對標準輸入和輸出流提供有限的緩衝區大小,如果讀寫子進程的輸出流或
輸入流迅速出現失敗,則可能導致子進程阻塞,甚至產生死鎖。 [email protected]

當沒有 Process 對象的更多引用時,不是刪掉子進程,而是繼續異步執行子進程。 

對於帶有 Process 對象的 Java 進程,沒有必要異步或併發執行由 Process 對象表示的進程。

關鍵點:

  1. Process 類提供了執行從進程輸入、執行輸出到進程、等待進程完成、檢查進程的退出狀態以及銷燬(殺掉)進程的方法。 
  2. 當沒有 Process 對象的更多引用時,不是刪掉子進程,而是繼續異步執行子進程。 

  3. 對於帶有 Process 對象的 Java 進程,沒有必要異步或併發執行由 Process 對象表示的進程。(毛意思:就是單線程麼,不需要在建子進程來分擔任務量麼~~~~~~)

     

destroy

       殺掉該Process對象代表的進程。(還有個以強殺方法.是不是類似於kill -9 ~~~)

exitValue

        返回該Process對象代表的進程的出口值,值0表示正常退出,非0非正常。關於該方法,應該是返回System.exit(int)方法中的參數。

waitFor

        返回該Process對象代表的進程的出口值,值0表示正常退出,非0非正常。

        該方法很類似exitValue方法,但是他們有個取別很大的地方,那就是exitValue方法會直接返回一個值,但是當前程序有可能還在運行中,所以該值不一定是正確的,而本方法會一直等待Process對象代表的進程退出後才返回值,而且調用本方法的進程將會一直堵塞等待返回值。建議使用本方法!

 

 

關於獲取外部進程的日誌的問題

getErrorStream:是獲取的錯誤日誌(即error級別的日誌)

getInputStream:是獲取外部進程的一般性日誌如(info,warn,debug)

平時我們的一份完整的日誌應該是 info+error的日誌 纔算完整.所以應該把這2個流合併到一起展示,纔是完整的.方法如下

import java.io.*;


public class Test {

    public static void main(String[] args) throws IOException, InterruptedException {
	Runtime rt = Runtime.getRuntime();//獲得Runtime對象
	String arr[] = {"CLASSPATH=D://","Path=C:\\Program Files\\Java\\jdk1.8.0_131\\bin"};//執行exec時的環境變量
        
        //exec方法第一個參數是執行的命令,第二個參數是環境變量,第三個參數是工作目錄
	Process pr = rt.exec("cmd /c javac a.java && java a", arr, new File("D://"));
        
        //獲取輸出流並轉換成緩衝區
        BufferedWriter bout = new BufferedWriter(new OutputStreamWriter(pr.getOutputStream()));
        bout.write("1 2");//輸出數據
        bout.close();//關閉流
        
        //SequenceInputStream是一個串聯流,能夠把兩個流結合起來,通過該對象就可以將
        //getInputStream方法和getErrorStream方法獲取到的流一起進行查看了,當然也可以單獨操作
	SequenceInputStream sis = new SequenceInputStream(pr.getInputStream(), pr.getErrorStream());
	InputStreamReader inst = new InputStreamReader(sis, "GBK");//設置編碼格式並轉換爲輸入流
	BufferedReader br = new BufferedReader(inst);//輸入流緩衝區

	String res = null;
	StringBuilder sb = new StringBuilder();
	while ((res = br.readLine()) != null) {//循環讀取緩衝區中的數據
	    sb.append(res+"\n");
	}
	br.close();
	pr.waitFor();
	pr.destroy();
	System.out.print(sb);//輸出獲取的數據
    }
}

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章