一般我们在java中运行其它类中的方法时
无论是静态调用
还是动态调用
都是在当前的进程中执行的
也就是说
只有一个java虚拟机实例在运行
而有的时候
我们需要通过java代码启动多个java子进程
这样做虽然占用了一些系统资源
但会使程序更加稳定
因为新启动的程序是在不同的虚拟机进程中运行的
如果有一个进程发生异常
并不影响其它的子进程
在Java中我们可以使用两种方法来实现这种要求最简单的方法就是通过Runtime中的exec方法执行java classname如果执行成功这个方法返回一个Process对象如果执行失败将抛出一个IOException错误下面让我们来看一个简单的例子
// Testjava文件
import javaio*;
public class Test
{
public static void main(String[] args)
{
FileOutputStream fOut = new FileOutputStream(c:\Testtxt);
fOutclose();
Systemoutprintln(被调用成功!);
}
}
// Test_Execjava
public class Test_Exec
{
public static void main(String[] args)
{
Runtime run = RuntimegetRuntime();
Process p = runexec(java test);
}
}
通过java Test_Exec运行程序后发现在C盘多了个Testtxt文件但在控制台中并未出现被调用成功!的输出信息因此可以断定Test已经被执行成功但因为某种原因Test的输出信息未在Test_Exec的控制台中输出这个原因也很简单因为使用exec建立的是Test_Exec的子进程这个子进程并没有自己的控制台因此它并不会输出任何信息
如果要输出子进程的输出信息可以通过Process中的getInputStream得到子进程的输出流(在子进程中输出在父进程中就是输入)然后将子进程中的输出流从父进程的控制台输出具体的实现代码如下如示
// Test_Exec_Outjava
import javaio*;
public class Test_Exec_Out
{
public static void main(String[] args)
{
Runtime run = RuntimegetRuntime();
Process p = runexec(java test);
BufferedInputStream in = new BufferedInputStream(pgetInputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String s;
while ((s = brreadLine()) != null)
Systemoutprintln(s);
}
}
从上面的代码可以看出在Test_Exec_Outjava中通过按行读取子进程的输出信息然后在Test_Exec_Out中按每行进行输出 上面讨论的是如何得到子进程的输出信息那么除了输出信息还有输入信息既然子进程没有自己的控制台那么输入信息也得由父进程提供我们可以通过Process的getOutputStream方法来为子进程提供输入信息(即由父进程向子进程输入信息而不是由控制台输入信息)
我们可以看看如下的代码
// Test
java文件
import javaio*;
public class Test
{
public static void main(String[] args)
{
BufferedReader br = new BufferedReader(new InputStreamReader(Systemin));
Systemoutprintln(由父进程输入的信息 + brreadLine());
}
}
// Test_Exec_Injava
import javaio*;
public class Test_Exec_In
{
public static void main(String[] args)
{
Runtime run = RuntimegetRuntime();
Process p = runexec(java test);
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(pgetOutputStream()));
bwwrite(向子进程输出信息);
bwflush();
bwclose(); // 必须得关闭流否则无法向子进程中输入信息
// Systeminread();
}
}
从以上代码可以看出Test得到由Test_Exec_In发过来的信息并将其输出当你不加bwflash()和bwclose()时信息将无法到达子进程也就是说子进程进入阻塞状态但由于父进程已经退出了因此子进程也跟着退出了如果要证明这一点可以在最后加上Systeminread()然后通过任务管理器(在windows下)查看java进程你会发现如果加上bwflush()和bwclose()只有一个java进程存在如果去掉它们就有两个java进程存在这是因为如果将信息传给Test在得到信息后Test就退出了在这里有一点需要说明一下exec的执行是异步的并不会因为执行的某个程序阻塞而停止执行下面的代码因此可以在运行test后仍可以执行下面的代码
exec方法经过了多次的重载上面使用的只是它的一种重载它还可以将命令和参数分开如exec(javatest)可以写成exec(java test)exec还可以通过指定的环境变量运行不同配置的java虚拟机
除了使用Runtime的exec方法建立子进程外还可以通过ProcessBuilder建立子进程ProcessBuilder的使用方法如下
// Test_Exec_Outjava
import javaio*;
public class Test_Exec_Out
{
public static void main(String[] args)
{
ProcessBuilder pb = new ProcessBuilder(java test);
Process p = pbstart();
… …
}
}
在建立子进程上ProcessBuilder和Runtime类似不同的ProcessBuilder使用start()方法启动子进程而Runtime使用exec方法启动子进程得到Process后它们的操作就完全一样的
ProcessBuilder和Runtime一样也可设置可执行文件的环境信息工作目录等
ProcessBuilder pb = new ProcessBuilder(Command arg arg );
// 设置环境变量
Map env = pbenvironment();
envput(key value);
envremove(key);
envput(key envget(key) + _test);
pbdirectory(\abcd); // 设置工作目录
Process p = pbstart(); // 建立子进程