Java的多进程运行模式分析

通常咱们在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

相关文章
相关标签/搜索