cmd 查看进程的子进程_如何在 Java 中运行子进程

(给ImportNew加星标,提高Java技能)

编译:ImportNew/唐尤华

dzone.com/articles/running-a-java-class-as-a-subprocess

本文介绍了如何在 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 jvmArgs, List 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 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 重定向,确定子进程在后台运行或阻塞运行。看起来像下面这样:

```javapublic static ProcessBuilder exec(Class clazz, List jvmArgs, List 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 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 参数,比如子进程内存大小。传统的直接调用无法对此进行配置。

如果喜欢这篇文章或者觉得有帮助,欢迎关注与分享([@lankydandev][2])!

[2]:https://twitter.com/LankyDanDev

推荐阅读

(点击标题可跳转阅读)

JVM 解剖公园:局部变量可用性

HashMap 实现原理解读

Java 设计模式之责任链模式实现的三种方式

看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

89f803dddb1fb2504f6b20da6ef9250e.png

好文章,我在看❤️

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值