Java創(chuàng)建進程
1 進程的概念 1
1.1 進程的概念 1
1.2 進程的特征 1
1.3 進程與線程區(qū)別 1
2 進程的創(chuàng)建 1
2.1 JAVA進程的創(chuàng)建 1
2.1.1 ProcessBuilder 2
2.1.2 Runtime 3
2.1.3 Process 4
2.2 實例 5
2.2.1 創(chuàng)建子進程 5
2.2.2 進程阻塞問題 7
2.2.3 在java中執(zhí)行java程序 11
1 進程的概念
1.1 進程的概念
進程是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ);是一個正在執(zhí)行的程序;計算機中正在運行的程序?qū)嵗?;可以分配給處理器并由處理器執(zhí)行的一個實體;由單一順序的執(zhí)行顯示,一個當前狀態(tài)和一組相關(guān)的系統(tǒng)資源所描述的活動單元。
第一,進程是一個實體。每一個進程都有它自己的地址空間,一般情況下,包括文本區(qū)域(text region)、數(shù)據(jù)區(qū)域(data region)和堆棧(stack region)。文本區(qū)域存儲處理器執(zhí)行的代碼;數(shù)據(jù)區(qū)域存儲變量和進程執(zhí)行期間使用的動態(tài)分配的內(nèi)存;堆棧區(qū)域存儲著活動過程調(diào)用的指令和本地變量。
第 二,進程是一個“執(zhí)行中的程序”。程序是一個沒有生命的實體,只有處理器賦予程序生命時,它才能成為一個活動的實體,我們稱其為進程。
1.2 進程的特征
動態(tài)性:進程的實質(zhì)是程序在多道程序系統(tǒng)中的一次執(zhí)行過程,進程是動態(tài)產(chǎn)生,動態(tài)消亡的。
并發(fā)性:任何進程都可以同其他進程一起并發(fā)執(zhí)行
獨立性:進程是一個能獨立運行的基本單位,同時也是系統(tǒng)分配資源和調(diào)度的獨立單位;
異步性:由于進程間的相互制約,使進程具有執(zhí)行的間斷性,即進程按各自獨立的、不可預(yù)知的速度向前推進
結(jié)構(gòu)特征:進程由程序、數(shù)據(jù)和進程控制塊三部分組成。
多個不同的進程可以包含相同的程序:一個程序在不同的數(shù)據(jù)集里就構(gòu)成不同的進程,能得到不同的結(jié)果;但是執(zhí)行過程中,程序不能發(fā)生改變。
1.3 進程與線程區(qū)別
進程和線程的主要差別在于它們是不同的操作系統(tǒng)資源管理方式。進程有獨立的地址空間,一個進程崩潰后,在保護模式下不會對其它進程產(chǎn)生影 響,而線程只是一個進程中的不同執(zhí)行路徑。線程有自己的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等于整個進程死掉,所以多進程的程 序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。但對于一些要求同時進行并且又要共享某些變量的并發(fā)操作,只能用線程,不能用進
程。
2 進程的創(chuàng)建
2.1 Java進程的創(chuàng)建
Java提供了兩種方法用來啟動進程或其它程序:
(1)使用Runtime的exec()方法
(2)使用ProcessBuilder的start()方法
2.1.1 ProcessBuilder
ProcessBuilder類是J2SE 1.5在java.lang中新添加的一個新類,此類用于創(chuàng)建操作系統(tǒng)進程,它提供一種啟動和管理進程(也就是應(yīng)用程序)的方法。在J2SE 1.5之前,都是由Process類處來實現(xiàn)進程的控制管理。
每個 ProcessBuilder 實例管理一個進程屬性集。start() 方法利用這些屬性創(chuàng)建一個新的 Process 實例。start() 方法可以從同一實例重復(fù)調(diào)用,以利用相同的或相關(guān)的屬性創(chuàng)建新的子進程。
每個進程生成器管理這些進程屬性:
命令 是一個字符串列表,它表示要調(diào)用的外部程序文件及其參數(shù)(如果有)。在此,表示有效的操作系統(tǒng)命令的字符串列表是依賴于系統(tǒng)的。例如,每一個總體變量,通常都要成為此列表中的元素,但有一些操作系統(tǒng),希望程序能自己標記命令行字符串——在這種系統(tǒng)中,Java 實現(xiàn)可能需要命令確切地包含這兩個元素。
環(huán)境 是從變量 到值 的依賴于系統(tǒng)的映射。初始值是當前進程環(huán)境的一個副本(請參閱 System.getenv())。
工作目錄。默認值是當前進程的當前工作目錄,通常根據(jù)系統(tǒng)屬性 user.dir 來命名。
redirectErrorStream 屬性。最初,此屬性為 false,意思是子進程的標準輸出和錯誤輸出被發(fā)送給兩個獨立的流,這些流可以通過 Process.getInputStream() 和 Process.getErrorStream() 方法來訪問。如果將值設(shè)置為 true,標準錯誤將與標準輸出合并。這使得關(guān)聯(lián)錯誤消息和相應(yīng)的輸出變得更容易。在此情況下,合并的數(shù)據(jù)可從
Process.getInputStream() 返回的流讀取,而從 Process.getErrorStream() 返回的流讀取將直接到達文件尾。
修改進程構(gòu)建器的屬性將影響后續(xù)由該對象的 start() 方法啟動的進程,但從不會影響以前啟動的進程或 Java 自身的進程。大多數(shù)錯誤檢查由 start() 方法執(zhí)行??梢孕薷膶ο蟮臓顟B(tài),但這樣 start() 將會失敗。例如,將命令屬性設(shè)置為一個空列表將不會拋出異常,除非包含了 start()。
注意,此類不是同步的。如果多個線程同時訪問一個 ProcessBuilder,而其中至少一個線程從結(jié)構(gòu)上修改了其中一個屬性,它必須 保持外部同步。
Java代碼 
-
構(gòu)造方法摘要
-
ProcessBuilder(List<String> command)
-
利用指定的操作系統(tǒng)程序和參數(shù)構(gòu)造一個進程生成器。
-
ProcessBuilder(String... command)
-
利用指定的操作系統(tǒng)程序和參數(shù)構(gòu)造一個進程生成器。
-
-
方法摘要
-
List<String> command()
-
返回此進程生成器的操作系統(tǒng)程序和參數(shù)。
-
ProcessBuilder command(List<String> command)
-
設(shè)置此進程生成器的操作系統(tǒng)程序和參數(shù)。
-
ProcessBuilder command(String... command)
-
設(shè)置此進程生成器的操作系統(tǒng)程序和參數(shù)。
-
File directory()
-
返回此進程生成器的工作目錄。
-
ProcessBuilder directory(File directory)
-
設(shè)置此進程生成器的工作目錄。
-
Map<String,String> environment()
-
返回此進程生成器環(huán)境的字符串映射視圖。
-
boolean redirectErrorStream()
-
通知進程生成器是否合并標準錯誤和標準輸出。
-
ProcessBuilder redirectErrorStream(boolean redirectErrorStream)
-
設(shè)置此進程生成器的 redirectErrorStream 屬性。
-
Process start()
-
使用此進程生成器的屬性啟動一個新進程。
2.1.2 Runtime
每個 Java 應(yīng)用程序都有一個 Runtime 類實例,使應(yīng)用程序能夠與其運行的環(huán)境相連接??梢酝ㄟ^ getRuntime 方法獲取當前運行時。
應(yīng)用程序不能創(chuàng)建自己的 Runtime 類實例。但可以通過 getRuntime 方法獲取當前Runtime運行時對象的引用。一旦得到了一個當前的Runtime對象的引用,就可以調(diào)用Runtime對象的方法去控制Java虛擬機的狀態(tài)和行為。
Java代碼 
-
void addShutdownHook(Thread hook)
-
注冊新的虛擬機來關(guān)閉掛鉤。
-
int availableProcessors()
-
向 Java 虛擬機返回可用處理器的數(shù)目。
-
Process exec(String command)
-
在單獨的進程中執(zhí)行指定的字符串命令。
-
Process exec(String[] cmdarray)
-
在單獨的進程中執(zhí)行指定命令和變量。
-
Process exec(String[] cmdarray, String[] envp)
-
在指定環(huán)境的獨立進程中執(zhí)行指定命令和變量。
-
Process exec(String[] cmdarray, String[] envp, File dir)
-
在指定環(huán)境和工作目錄的獨立進程中執(zhí)行指定的命令和變量。
-
Process exec(String command, String[] envp)
-
在指定環(huán)境的單獨進程中執(zhí)行指定的字符串命令。
-
Process exec(String command, String[] envp, File dir)
-
在有指定環(huán)境和工作目錄的獨立進程中執(zhí)行指定的字符串命令。
-
void exit(int status)
-
通過啟動虛擬機的關(guān)閉序列,終止當前正在運行的 Java 虛擬機。
-
long freeMemory()
-
返回 Java 虛擬機中的空閑內(nèi)存量。
-
void gc()
-
運行垃圾回收器。
-
InputStream getLocalizedInputStream(InputStream in)
-
已過時。 從 JDK 1.1 開始,將本地編碼字節(jié)流轉(zhuǎn)換為 Unicode 字符流的首選方法是使用 InputStreamReader 和 BufferedReader 類。
-
OutputStream getLocalizedOutputStream(OutputStream out)
-
已過時。 從 JDK 1.1 開始,將 Unicode 字符流轉(zhuǎn)換為本地編碼字節(jié)流的首選方法是使用 OutputStreamWriter、BufferedWriter 和 PrintWriter 類。
-
static Runtime getRuntime()
-
返回與當前 Java 應(yīng)用程序相關(guān)的運行時對象。
-
void halt(int status)
-
強行終止目前正在運行的 Java 虛擬機。
-
void load(String filename)
-
加載作為動態(tài)庫的指定文件名。
-
void loadLibrary(String libname)
-
加載具有指定庫名的動態(tài)庫。
-
long maxMemory()
-
返回 Java 虛擬機試圖使用的最大內(nèi)存量。
-
boolean removeShutdownHook(Thread hook)
-
取消注冊某個先前已注冊的虛擬機關(guān)閉掛鉤。
-
void runFinalization()
-
運行掛起 finalization 的所有對象的終止方法。
-
static void runFinalizersOnExit(boolean value)
-
已過時。 此方法本身具有不安全性。它可能對正在使用的對象調(diào)用終結(jié)方法,而其他線程正在操作這些對象,從而導(dǎo)致不正確的行為或死鎖。
-
long totalMemory()
-
返回 Java 虛擬機中的內(nèi)存總量。
-
void traceInstructions(boolean on)
-
啟用/禁用指令跟蹤。
-
void traceMethodCalls(boolean on)
-
啟用/禁用方法調(diào)用跟蹤。
2.1.3 Process
不管通過那種方法啟動進程后,都會返回一個Process類的實例代表啟動的進程,該實例可用來控制進程并獲得相關(guān)信息。Process 類提供了執(zhí)行從進程輸入、執(zhí)行輸出到進程、等待進程完成、檢查進程的退出狀態(tài)以及銷毀(殺掉)進程的方法:
Java代碼 
-
void destroy()
-
殺掉子進程。
-
一般情況下,該方法并不能殺掉已經(jīng)啟動的進程,不用為好。
-
int exitValue()
-
返回子進程的出口值。
-
只有啟動的進程執(zhí)行完成、或者由于異常退出后,exitValue()方法才會有正常的返回值,否則拋出異常。
-
InputStream getErrorStream()
-
獲取子進程的錯誤流。
-
如果錯誤輸出被重定向,則不能從該流中讀取錯誤輸出。
-
InputStream getInputStream()
-
獲取子進程的輸入流。
-
可以從該流中讀取進程的標準輸出。
-
OutputStream getOutputStream()
-
獲取子進程的輸出流。
-
寫入到該流中的數(shù)據(jù)作為進程的標準輸入。
-
int waitFor()
-
導(dǎo)致當前線程等待,如有必要,一直要等到由該 Process 對象表示的進程已經(jīng)終止。
通過該類提供的方法,可以實現(xiàn)與啟動的進程之間通信,達到交互的目的。
2.2 實例
2.2.1 創(chuàng)建子進程
要創(chuàng)建子進程可以通過使用使用ProcessBuilder的start()方法和Runtime的exec()方法。
(1)Runtime.exec()
Java代碼 
-
import java.io.BufferedReader;
-
import java.io.File;
-
import java.io.InputStreamReader;
-
-
public class Test1 {
-
public static void main(String[] args) {
-
try {
-
Process p = null;
-
String line = null;
-
BufferedReader stdout = null;
-
-
//list the files and directorys under C:\
-
p = Runtime.getRuntime().exec("CMD.exe /C dir", null, new File("C:\\"));
-
stdout = new BufferedReader(new InputStreamReader(p
-
.getInputStream()));
-
while ((line = stdout.readLine()) != null) {
-
System.out.println(line);
-
}
-
stdout.close();
-
-
//echo the value of NAME
-
p = Runtime.getRuntime().exec("CMD.exe /C echo %NAME%", new String[] {"NAME=TEST"});
-
stdout = new BufferedReader(new InputStreamReader(p
-
.getInputStream()));
-
while ((line = stdout.readLine()) != null) {
-
System.out.println(line);
-
}
-
stdout.close();
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
-
}
(2)ProcessBuilder
Java代碼 
-
import java.io.BufferedReader;
-
import java.io.File;
-
import java.io.InputStreamReader;
-
import java.util.ArrayList;
-
import java.util.List;
-
-
public class Test2 {
-
public static void main(String[] args) {
-
try {
-
List<String> list = new ArrayList<String>();
-
ProcessBuilder pb = null;
-
Process p = null;
-
String line = null;
-
BufferedReader stdout = null;
-
-
//list the files and directorys under C:\
-
list.add("CMD.EXE");
-
list.add("/C");
-
list.add("dir");
-
pb = new ProcessBuilder(list);
-
pb.directory(new File("C:\\"));
-
p = pb.start();
-
-
stdout = new BufferedReader(new InputStreamReader(p
-
.getInputStream()));
-
while ((line = stdout.readLine()) != null) {
-
System.out.println(line);
-
}
-
stdout.close();
-
-
//echo the value of NAME
-
pb = new ProcessBuilder();
-
pb.command(new String[] {"CMD.exe", "/C", "echo %NAME%"});
-
pb.environment().put("NAME", "TEST");
-
p = pb.start();
-
-
stdout = new BufferedReader(new InputStreamReader(p
-
.getInputStream()));
-
while ((line = stdout.readLine()) != null) {
-
System.out.println(line);
-
}
-
stdout.close();
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
-
}
從啟動其他程序的Java進程看,已啟動的其他程序輸出就是一個普通的輸入流,可以通過getInputStream()和getErrorStream來獲取。對于一般輸出文本的進程來說,可以將InputStream封裝成BufferedReader,然后就可以一行一行的對進程的標準輸出進行處理。
通常,一個程序/進程在執(zhí)行結(jié)束后會向操作系統(tǒng)返回一個整數(shù)值,0一般代表執(zhí)行成功,非0表示執(zhí)行出現(xiàn)問題。有兩種方式可以用來獲取進程的返回值。一是利用waitFor(),該方法是阻塞的,執(zhí)導(dǎo)進程執(zhí)行完成后再返回。該方法返回一個代表進程返回值的整數(shù)值。另一個方法是調(diào)用exitValue()方法,該方法是非阻塞的,調(diào)用立即返回。但是如果進程沒有執(zhí)行完成,則拋出異常。
2.2.2 進程阻塞問題
由Process代表的進程在某些平臺上有時候并不能很好的工作,特別是在對代表進程的標準輸入流、輸出流和錯誤輸出進行操作時,如果使用不慎,有可能導(dǎo)致進程阻塞,甚至死鎖。
如果將以上事例中的從標準輸出重讀取信息的語句修改為從錯誤輸出流中讀取:
stdout = new BufferedReader(new InputStreamReader(p.getErrorStream()));
那么程序?qū)l(fā)生阻塞,不能執(zhí)行完成,而是hang在那里。
當進程啟動后,就會打開標準輸出流和錯誤輸出流準備輸出,當進程結(jié)束時,就會關(guān)閉他們。在以上例子中,錯誤輸出流沒有數(shù)據(jù)要輸出,標準輸出流中有數(shù)據(jù)輸出。由于標準輸出流中的數(shù)據(jù)沒有被讀取,進程就不會結(jié)束,錯誤輸出流也就不會被關(guān)閉,因此在調(diào)用readLine()方法時,整個程序就會被阻塞。為了解決這個問題,可以根據(jù)輸出的實際先后,先讀取標準輸出流,然后讀取錯誤輸出流。
但是,很多時候不能很明確的知道輸出的先后,特別是要操作標準輸入的時候,情況就會更為復(fù)雜。這時候可以采用線程來對標準輸出、錯誤輸出和標準輸入進行分別處理,根據(jù)他們之間在業(yè)務(wù)邏輯上的關(guān)系決定讀取那個流或者寫入數(shù)據(jù)。
針對標準輸出流和錯誤輸出流所造成的問題,可以使用ProcessBuilder的redirectErrorStream()方法將他們合二為一,這時候只要讀取標準輸出的數(shù)據(jù)就可以了。
當在程序中使用Process的waitFor()方法時,特別是在讀取之前調(diào)用waitFor()方法時,也有可能造成阻塞。可以用線程的方法來解決這個問題,也可以在讀取數(shù)據(jù)后,調(diào)用waitFor()方法等待程序結(jié)束。
總之,解決阻塞的方法應(yīng)該有兩種:
(1)使用ProcessBuilder類,利用redirectErrorStream方法將標準輸出流和錯誤輸出流合二為一,在用start()方法啟動進程后,先從標準輸出中讀取數(shù)據(jù),然后調(diào)用waitFor()方法等待進程結(jié)束。
如:
Java代碼 
-
import java.io.BufferedReader;
-
import java.io.File;
-
import java.io.InputStreamReader;
-
import java.util.ArrayList;
-
import java.util.List;
-
-
public class Test3 {
-
public static void main(String[] args) {
-
try {
-
List<String> list = new ArrayList<String>();
-
ProcessBuilder pb = null;
-
Process p = null;
-
String line = null;
-
BufferedReader stdout = null;
-
//list the files and directorys under C:\
-
list.add("CMD.EXE");
-
list.add("/C");
-
list.add("dir1");
-
pb = new ProcessBuilder(list);
-
pb.directory(new File("C:\\"));
-
//merge the error output with the standard output
-
pb.redirectErrorStream(true);
-
p = pb.start();
-
//read the standard output
-
stdout = new BufferedReader(new InputStreamReader(p
-
.getInputStream()));
-
while ((line = stdout.readLine()) != null) {
-
System.out.println(line);
-
}
-
int ret = p.waitFor();
-
System.out.println("the return code is " + ret);
-
stdout.close();
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
-
}
(2)使用線程
Java代碼 
-
import java.util.*;
-
import java.io.*;
-
-
class StreamWatch extends Thread {
-
InputStream is;
-
String type;
-
List<String> output = new ArrayList<String>();
-
boolean debug = false;
-
StreamWatch(InputStream is, String type) {
-
this(is, type, false);
-
}
-
-
StreamWatch(InputStream is, String type, boolean debug) {
-
this.is = is;
-
this.type = type;
-
this.debug = debug;
-
}
-
-
public void run() {
-
try {
-
PrintWriter pw = null;
-
InputStreamReader isr = new InputStreamReader(is);
-
BufferedReader br = new BufferedReader(isr);
-
String line = null;
-
while ((line = br.readLine()) != null) {
-
output.add(line);
-
if (debug)
-
System.out.println(type + ">" + line);
-
}
-
if (pw != null)
-
pw.flush();
-
} catch (IOException ioe) {
-
ioe.printStackTrace();
-
}
-
}
-
-
public List<String> getOutput() {
-
return output;
-
}
-
}
Java代碼 
-
public class Test5 {
-
public static void main(String args[]) {
-
try {
-
List<String> list = new ArrayList<String>();
-
ProcessBuilder pb = null;
-
Process p = null;
-
// list the files and directorys under C:\
-
list.add("CMD.EXE");
-
list.add("/C");
-
list.add("dir1");
-
pb = new ProcessBuilder(list);
-
pb.directory(new File("C:\\"));
-
p = pb.start();
-
-
// process error and output message
-
StreamWatch errorWatch = new StreamWatch(p.getErrorStream(),
-
"ERROR");
-
StreamWatch outputWatch = new StreamWatch(p.getInputStream(),
-
"OUTPUT");
-
// start to watch
-
errorWatch.start();
-
outputWatch.start();
-
//wait for exit
-
int exitVal = p.waitFor();
-
//print the content from ERROR and OUTPUT
-
System.out.println("ERROR: " + errorWatch.getOutput());
-
System.out.println("OUTPUT: " + outputWatch.getOutput());
-
System.out.println("the return code is " + exitVal);
-
} catch (Throwable t) {
-
t.printStackTrace();
-
}
-
}
-
}
2.2.3 在java中執(zhí)行java程序
執(zhí)行一個Java程序的關(guān)鍵在于:
(1)知道JAVA虛擬機的位置,即java.exe或者java的路徑
(2)知道要執(zhí)行的java程序的位置
(3)知道該程序所依賴的其他類的位置
舉一個例子,一目了然。
(1)待執(zhí)行的Java類
Java代碼 
-
public class MyTest {
-
public static void main(String[] args) {
-
System.out.println("OUTPUT one");
-
System.out.println("OUTPUT two");
-
System.err.println("ERROR 1");
-
System.err.println("ERROR 2");
-
for(int i = 0; i < args.length; i++)
-
{
-
System.out.printf("args[%d] = %s.", i, args[i]);
-
}
-
}
-
}
(2)執(zhí)行該類的程序
Java代碼 
-
import java.util.*;
-
import java.io.*;
-
-
class StreamWatch extends Thread {
-
InputStream is;
-
String type;
-
List<String> output = new ArrayList<String>();
-
boolean debug = false;
-
-
StreamWatch(InputStream is, String type) {
-
this(is, type, false);
-
}
-
-
StreamWatch(InputStream is, String type, boolean debug) {
-
this.is = is;
-
this.type = type;
-
this.debug = debug;
-
}
-
-
public void run() {
-
try {
-
PrintWriter pw = null;
-
InputStreamReader isr = new InputStreamReader(is);
-
BufferedReader br = new BufferedReader(isr);
-
String line = null;
-
while ((line = br.readLine()) != null) {
-
output.add(line);
-
if (debug)
-
System.out.println(type + ">" + line);
-
}
-
if (pw != null)
-
pw.flush();
-
} catch (IOException ioe) {
-
ioe.printStackTrace();
-
}
-
}
-
-
public List<String> getOutput() {
-
return output;
-
}
-
}
Java代碼 
-
public class Test6 {
-
public static void main(String args[]) {
-
try {
-
List<String> list = new ArrayList<String>();
-
ProcessBuilder pb = null;
-
Process p = null;
-
-
String java = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
-
String classpath = System.getProperty("java.class.path");
-
// list the files and directorys under C:\
-
list.add(java);
-
list.add("-classpath");
-
list.add(classpath);
-
list.add(MyTest.class.getName());
-
list.add("hello");
-
list.add("world");
-
list.add("good better best");
-
-
pb = new ProcessBuilder(list);
-
p = pb.start();
-
-
System.out.println(pb.command());
-
// process error and output message
-
StreamWatch errorWatch = new StreamWatch(p.getErrorStream(),
-
"ERROR");
-
StreamWatch outputWatch = new StreamWatch(p.getInputStream(),
-
"OUTPUT");
-
// start to watch
-
errorWatch.start();
-
outputWatch.start();
-
//wait for exit
-
int exitVal = p.waitFor();
-
//print the content from ERROR and OUTPUT
-
System.out.println("ERROR: " + errorWatch.getOutput());
-
System.out.println("OUTPUT: " + outputWatch.getOutput());
-
System.out.println("the return code is " + exitVal);
-
} catch (Throwable t) {
-
t.printStackTrace();
-
}
|