Java的多进程运行模式分析
生活随笔
收集整理的這篇文章主要介紹了
Java的多进程运行模式分析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本文曾發表于天極網:http://dev.yesky.com/284/2659284.shtml
??? 一般我們在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<String,?String>?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中運行其它類中的方法時,無論是靜態調用,還是動態調用,都是在當前的進程中執行的,也就是說,只有一個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<String,?String>?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的多进程运行模式分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 梦到吃豆腐是什么意思周公解梦
- 下一篇: 女人梦到狗抓伤是什么意思