通常咱們在java中運行其它類中的方法時,不管是靜態調用,仍是動態調用,都是在當前的進程中執行的,也就是說,只有一個java虛擬機實例在運行。而 有的時候,咱們須要經過java代碼啓動多個java子進程。這樣作雖然佔用了一些系統資源,但會使程序更加穩定,由於新啓動的程序是在不一樣的虛擬機進程 中運行的,若是有一個進程發生異常,並不影響其它的子進程。
在Java中咱們可使用兩種方法來實現這種要求。最簡單的方法就是經過 Runtime中的exec方法執行java classname。若是執行成功,這個方法返回一個Process對象,若是執行失敗,將拋出一個IOException錯誤。下面讓咱們來看一個簡單 的例子。
// Test1.java文件
import java.io.*;
public class Test
{
public static void main(String[] args)
{
FileOutputStream fOut = new FileOutputStream("c:\\Test1.txt");
fOut.close();
System.out.println("被調用成功!");
}
}
// Test_Exec.java
public class Test_Exec
{
public static void main(String[] args)
{
Runtime run = Runtime.getRuntime();
Process p = run.exec("java test1");
}
}
通 過java Test_Exec運行程序後,發如今C盤多了個Test1.txt文件,但在控制檯中並未出現"被調用成功!"的輸出信息。所以能夠判定,Test已經 被執行成功,但由於某種緣由,Test的輸出信息未在Test_Exec的控制檯中輸出。這個緣由也很簡單,由於使用exec創建的是Test_Exec 的子進程,這個子進程並無本身的控制檯,所以,它並不會輸出任何信息。
若是要輸出子進程的輸出信息,能夠經過Process中的getInputStream獲得子進程的輸出流(在子進程中輸出,在父進程中就是輸入),而後將子進程中的輸出流從父進程的控制檯輸出。具體的實現代碼以下如示:
// Test_Exec_Out.java
import java.io.*;
public class Test_Exec_Out
{
public static void main(String[] args)
{
Runtime run = Runtime.getRuntime();
Process p = run.exec("java test1");
BufferedInputStream in = new BufferedInputStream(p.getInputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String s;
while ((s = br.readLine()) != null)
System.out.println(s);
}
}
從 上面的代碼能夠看出,在Test_Exec_Out.java中經過按行讀取子進程的輸出信息,而後在Test_Exec_Out中按每行進行輸出。上面 討論的是如何獲得子進程的輸出信息。那麼,除了輸出信息,還有輸入信息。既然子進程沒有本身的控制檯,那麼輸入信息也得由父進程提供。咱們能夠經過 Process的getOutputStream方法來爲子進程提供輸入信息(即由父進程向子進程輸入信息,而不是由控制檯輸入信息)。咱們能夠看看以下 的代碼:
// Test2.java文件
import java.io.*;
public class Test
{
public static void main(String[] args)
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("由父進程輸入的信息:" + br.readLine());
}
}
// Test_Exec_In.java
import java.io.*;
public class Test_Exec_In
{
public static void main(String[] args)
{
Runtime run = Runtime.getRuntime();
Process p = run.exec("java test2");
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
bw.write("向子進程輸出信息");
bw.flush();
bw.close(); // 必須得關閉流,不然沒法向子進程中輸入信息
// System.in.read();
}
}
從 以上代碼能夠看出,Test1獲得由Test_Exec_In發過來的信息,並將其輸出。當你不加bw.flash()和bw.close()時,信息將 沒法到達子進程,也就是說子進程進入阻塞狀態,但因爲父進程已經退出了,所以,子進程也跟着退出了。若是要證實這一點,能夠在最後加上 System.in.read(),而後經過任務管理器(在windows下)查看java進程,你會發現若是加上bw.flush()和 bw.close(),只有一個java進程存在,若是去掉它們,就有兩個java進程存在。這是由於,若是將信息傳給Test2,在獲得信息後, Test2就退出了。在這裏有一點須要說明一下,exec的執行是異步的,並不會由於執行的某個程序阻塞而中止執行下面的代碼。所以,能夠在運行 test2後,仍能夠執行下面的代碼。
exec方法通過了屢次的重載。上面使用的只是它的一種重載。它還能夠將命令和參數分開,如exec("java.test2")能夠寫成exec("java", "test2")。exec還能夠經過指定的環境變量運行不一樣配置的java虛擬機。
除了使用Runtime的exec方法創建子進程外,還能夠經過ProcessBuilder創建子進程。ProcessBuilder的使用方法以下:
// Test_Exec_Out.java
import java.io.*;
public class Test_Exec_Out
{
public static void main(String[] args)
{
ProcessBuilder pb = new ProcessBuilder("java", "test1");
Process p = pb.start();
… …
}
}
在創建子進程上,ProcessBuilder和Runtime相似,不一樣的ProcessBuilder使用start()方法啓動子進程,而Runtime使用exec方法啓動子進程。獲得Process後,它們的操做就徹底同樣的。
ProcessBuilder和Runtime同樣,也可設置可執行文件的環境信息、工做目錄等。下面的例子描述瞭如何使用ProcessBuilder設置這些信息。
ProcessBuilder pb = new ProcessBuilder("Command", "arg2", "arg2", ''');
// 設置環境變量
Map env = pb.environment();
env.put("key1", "value1");
env.remove("key2");
env.put("key2", env.get("key1") + "_test");
pb.directory("..\abcd"); // 設置工做目錄
Process p = pb.start(); // 創建子進程java