1. 程式人生 > >Java中Runtime執行時環境機制總結

Java中Runtime執行時環境機制總結

最近由於在編碼中需要在java程式碼中執行linux命令,使用到了Runtime類的一些方法,也出現幾個小bug,所以趁這個機會對Runtime也就是執行時環境這個類進行總結。

Runtime.getRuntime()能得到一個Runtime物件例項,也就是當前執行時環境例項,這個玩藝是什麼東西?java中稱為虛擬機器的執行時環境,這個說法很抽象,我在網上百度了很久,沒有確切的說法,我感覺這個Runtime物件例項其實更像是java 程式的程序的概念,當然只是我初步的想法,後續找到更多資料可能就發現錯了。

1.去看下Runtime的原始碼,首先可以發現Runtime使用了單例的設計模式

private static Runtime currentRuntime = newRuntime();

public static Runtime getRuntime() {

       return currentRuntime;

    }

private Runtime() {}

--上面就是典型的單例項設計模式,所以在java程式中不同執行緒通過呼叫Runtime.getRuntime()獲得的是同一個物件例項,也就是說一個java程序中只有一個Runtime例項

2. Runtime的一些方法

主要使用的方法就是exec,在Runtime中,exec方法進行了多次過載

public Process exec(String command) throwsIOException

public Process exec(String command,String[] envp)

public Process exec(String command,String[] envp, File dir)

public Process exec(String cmdarray[])throws IOException

比較多用到的是第一個和第四個,第三個有兩個引數String[] envp, File dir,這是什麼?前者是指環境變數字串,後者是指工作目錄。這一點跟程序很相似。不指定這兩個引數時預設是當前環境和當前目錄。再看看這個方法的返回值是Process型別,這個是什麼?其實是子程序的一個管理器例項,也就是說我們在呼叫exec方法時,實際上是建立了一個子程序,並且由該子程序執行我們傳入的命令。這點可以明顯從exec原始碼看出

public Process exec(String[] cmdarray,String[] envp, File dir)

       throws IOException {

       return new ProcessBuilder(cmdarray)

           .environment(envp)

           .directory(dir)

           .start();

}

-- ProcessBuilder建立子程序,並設定環境變數和工作目錄,然後start。可見父程序子程序的概念在java中還是存在的。

3. Process的主要方法

destroy()

   殺掉子程序。

 exitValue()

   返回子程序的出口值。

 InputStream getErrorStream()

   獲得子程序的錯誤流。

 InputStream getInputStream()

   獲得子程序的輸入流。

 OutputStream getOutputStream()

   獲得子程序的輸出流。

 waitFor()

   導致當前執行緒等待,如果必要,一直要等到由該 Process 物件表示的程序已經終止。

--一般執行linux命令使用比較多的是waitFor(),諸塞等待子程序完成該linux命令。然後再繼續執行緒後續的程式碼。

--以上基本簡單把Runtime的應用機制介紹了下,雖然不算深入,但是平常簡單的呼叫linux命令這種簡單的工作就能做到基本心裡有底了,下面繼續說說在應用過程中遇到的小坑

坑一:如何在exec執行多條linux命令

不熟悉時可能會把一段linux命令當作輸入直接執行,然後發現並沒有執行,這是為什麼呢?比如”mv dir1/*.* dir2;wait;gzip dir2/*.*;” 這段命令直接在linux上直接執行是沒問題的,但是放到exec執行會發現根本沒能執行,原因是exec最終會對command進行一次字串分割,預設以” \t\n\r\f”(前有一個空格,引號不是)為分割符,上面語句不符合格式。

public Process exec(String command,String[] envp, File dir)

       throws IOException {

       if (command.length() == 0)

           throw new IllegalArgumentException("Empty command");



       StringTokenizer st = new StringTokenizer(command);

       String[] cmdarray = new String[st.countTokens()];

       for (int i = 0; st.hasMoreTokens(); i++)

           cmdarray[i] = st.nextToken();

       return exec(cmdarray, envp, dir);

}

正確用法是用&&連線起來”mv dir1/*.* dir2&&wait&&gzip dir2/*.*”,或則把三個命令加入String陣列,呼叫publicProcess exec(String cmdarray[]) throws IOException這個過載方法。

坑二:為什麼如果在java程式碼中不呼叫waitFor()阻塞等待linux中命令執行結束會出現部分命令並沒有執行的情況。

比如我遇到的問題,在java中執行了下面兩行程式碼

Runtime.getRuntime().exec(“mv dir1/file.txtdir2”);

Runtime.getRuntime().exec(“gzip dir2/ file.txt”);

多執行緒執行一段時間後會發現有部分檔案沒有按希望去壓縮,這是什麼原因,百思不得其解,目前還沒搞得太清楚,初步認為是主程序建立了兩個子程序,如果不諸塞的話,可能當程序1的檔案還沒挪到dir2,程序2已經執行了,所以這部分檔案沒有按希望進行壓縮。但是為什麼程序二沒有報檔案不存在的錯誤呢?這個也是跟疑問。不管怎麼說解決之道就是加阻塞。

Runtime.getRuntime().exec(“mv dir1/file.txtdir2”) .waitFor();

Runtime.getRuntime().exec(“gzip dir2/ file.txt”).waitFor();

加上.waitFor()後,發現之前的問題解決了,檔案全部都進行了壓縮。

--補充修正

如何在exec執行多條linux命令

呼叫public Process exec(String cmdarray[]) throws IOException這個過載方法是無法執行多條命令的,陣列的作用只是讓你手工把命令和傳入引數切割開來,以便後續子程序的呼叫,另外”mv dir1/*.* dir2&&wait&&gzip dir2/*.*”這種用&&連線的多命令也是不可用的,第呼叫多條命令只能一個個命令執行,或則把多條命令寫入指令碼,exec直接執行該指令碼。