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直接執行該指令碼。