ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

如何在 Java 中运行子进程

2021-05-30 10:57:32  阅读:200  来源: 互联网

标签:Java ProcessBuilder List add command 进程 java 运行


本文介绍了如何在 Java 中运行子进程(非 jar)。确切地说,要求从测试程序内部启动一个新进程,而非直接在测试(进程)内部运行。尽管不是什么炫酷的技术,但以前没有做过类似的事情,不清楚如何下手。


经过一番搜索,在 Stack Overflow 中找到了[解答][1]。为了更好地解决问题,重写了答案。


[1]:https://stackoverflow.com/questions/636367/executing-a-java-application-in-a-separate-process


```java
class JavaProcess {
 private JavaProcess() {
 }

 public static int exec(Class clazz, List<String> jvmArgs, List<String> args) throws IOException,
       InterruptedException {
   String javaHome = System.getProperty("java.home");
   String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
   String classpath = System.getProperty("java.class.path");
   String className = clazz.getName();
   List<String> command = new ArrayList<>();
   command.add(javaBin);
   command.addAll(jvmArgs);
   command.add("-cp");
   command.add(classpath);
   command.add(className);
   command.addAll(args);
   ProcessBuilder builder = new ProcessBuilder(command);
   Process process = builder.inheritIO().start();
   process.waitFor();
   return process.exitValue();
 }
}
```


上面的静态函数接收的参数包括 `Class`、JVM 参数以及 `main` 方法执行参数,通过参数可以完全控制子进程执行过程,例如减小执行时 JVM 堆空间(本文目标之一)。


注意:实际运行时,需要提供 `main` 方法。


为了让主程序与子进程使用同一个 Java 版本,可以使用 `javaBin` 中的路径调用 Java 可执行程序。如果把 `javaBin` 替换为 `java`,会调用本机默认安装的 Java 版本。大多数情况不会有问题,但很可能出现意外。


所有 command 加载到 `command` 列表后,都会传给 `ProcessBuilder` 生成命令。`ProcessBuilder` 对 `command` 中每个参数用空格分隔。也可以调用重载的构造函数,直接手工传入整个命令字符串,无需手工添加字符串参数。


一般在 IO 传递给执行的主进程后会启动子进程。通常期望看到 `stdout` 和 `stderr` 输出。可以直接调用 `inheritIO`,也可以使用以下方法(额外配置了 `stdin` 子进程):


```java
builder
   .redirectInput(ProcessBuilder.Redirect.INHERIT)
   .redirectOutput(ProcessBuilder.Redirect.INHERIT)
   .redirectError(ProcessBuilder.Redirect.INHERIT);
```


最后,`waitFor` 会通知执行线程等待产生的子进程执行结束,无论执行成功或还是出现错误,只要子进程以某种方式结束就可以了。主程序会继续执行。子进程的结束状态可以通过进程结束时 `exitValue` 判断。例如,通常 0 代表执行成功,1 代表语法无效错误。根据每个应用不同,还有其他 exit code。


调用 `exec` 方法:


```java
JavaProcess.exec(MyProcess.class, List.of("-Xmx200m"), List.of("argument"))
```


> 译注:List.of 是 Java 9 引入的方法,之前的版本可以使用 Arrays.asList。


将执行下面的命令(或类似的命令):


```shell
/Library/Java/JavaVirtualMachines/jdk-12.0.1.jdk/Contents/Home/bin/java -cp /playing-around-for-blogs MyProcess "argument"
```


为了整洁起见,已经移除了包括 classpath 在内的许多路径。实际执行结果可能看起来要长许多,与具体应用相关。上面展示的命令是可运行最短路径(为本文的示例定制)。


`exec` 方法很灵活,非常有助于描述正在进行的工作。不仅如此,还可以返回 `ProcessBuilder` 对象,增强扩展性、适用更多场景。不但可以在多个地方重用代码段,还能灵活配置 IO 重定向,确定子进程在后台运行或阻塞运行。看起来像下面这样:


```java
public static ProcessBuilder exec(Class clazz, List<String> jvmArgs, List<String> args) {
 String javaHome = System.getProperty("java.home");
 String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
 String classpath = System.getProperty("java.class.path");
 String className = clazz.getName();
 List<String> command = new ArrayList<>();
 command.add(javaBin);
 command.addAll(jvmArgs);
 command.add("-cp");
 command.add(classpath);
 command.add(className);
 command.addAll(args);
 return new ProcessBuilder(command);
}
```


利用上面的函数,可以运行 classpath 上的任意 class。这篇文章中,用来在集成测试中启动新的子进程,无需提前构建 jar。这种方法可以控制 JVM 参数,比如子进程内存大小。传统的直接调用无法对此进行配置。


标签:Java,ProcessBuilder,List,add,command,进程,java,运行
来源: https://blog.51cto.com/u_15127686/2832720

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有