1. 概述
Java中的进程API在Java 5之前是相当原始的,生成新进程的唯一方法是使用Runtime.getRuntime().exec() API。然后在Java 5中,引入了ProcessBuilder API,它支持一种更简洁的生成新进程的方式。
Java 9添加了一种获取有关当前进程和任何派生进程的信息的新方法。
在本文中,我们将介绍这两项增强功能。
2. 当前Java进程信息
我们现在可以通过java.lang.ProcessHandle.Info API获取很多关于进程的信息:
- 用于启动进程的命令
- 命令的参数
- 进程开始的时刻
- 它花费的总时间和创建它的用户
以下是我们如何做到这一点:
private static void infoOfCurrentProcess() {
ProcessHandle processHandle = ProcessHandle.current();
ProcessHandle.Info processInfo = processHandle.info();
log.info("PID: " + processHandle.pid());
log.info("Arguments: " + processInfo.arguments());
log.info("Command: " + processInfo.command());
log.info("Instant: " + processInfo.startInstant());
log.info("Total CPU duration: " + processInfo.totalCpuDuration());
log.info("User: " + processInfo.user());
}
重要的是要注意java.lang.ProcessHandle.Info是在另一个接口java.lang.ProcessHandle中定义的公共接口,JDK提供者(Oracle JDK、Open JDK、Zulu或其他)应该为这些接口提供实现,以便这些实现返回进程的相关信息。
输出取决于操作系统和Java版本,下面是输出的示例:
16:31:24.784 [main] INFO c.b.j.process.ProcessAPIEnhancements - PID: 22640
16:31:24.790 [main] INFO c.b.j.process.ProcessAPIEnhancements - Arguments: Optional[[Ljava.lang.String;@2a17b7b6]
16:31:24.791 [main] INFO c.b.j.process.ProcessAPIEnhancements - Command: Optional[/Library/Java/JavaVirtualMachines/jdk-13.0.1.jdk/Contents/Home/bin/java]
16:31:24.795 [main] INFO c.b.j.process.ProcessAPIEnhancements - Instant: Optional[2021-08-31T14:31:23.870Z]
16:31:24.795 [main] INFO c.b.j.process.ProcessAPIEnhancements - Total CPU duration: Optional[PT0.818115S]
16:31:24.796 [main] INFO c.b.j.process.ProcessAPIEnhancements - User: Optional[username]
3. 生成的进程信息
也可以获取新生成的进程的进程信息。在这种情况下,在生成进程并获得java.lang.Process的实例之后,我们对其调用toHandle()方法以获取java.lang.ProcessHandle的实例。
其余细节与上一节相同:
String javaCmd = ProcessUtils.getJavaCmd().getAbsolutePath();
ProcessBuilder processBuilder = new ProcessBuilder(javaCmd, "-version");
Process process = processBuilder.inheritIO().start();
ProcessHandle processHandle = process.toHandle();
4. 枚举系统中的实时进程
我们可以列出当前系统中的所有进程,这些进程是当前进程可见的。返回的列表是调用API时的快照,因此有可能某些进程在捕获快照后终止或添加了一些新进程。
为此,我们可以使用java.lang.ProcessHandle接口中可用的静态方法allProcesses(),它返回一个ProcessHandle流:
private static void infoOfLiveProcesses() {
Stream<ProcessHandle> liveProcesses = ProcessHandle.allProcesses();
liveProcesses.filter(ProcessHandle::isAlive)
.forEach(ph -> {
log.info("PID: " + ph.pid());
log.info("Instance: " + ph.info().startInstant());
log.info("User: " + ph.info().user());
});
}
5. 枚举子进程
有两种变体可以做到这一点:
- 获取当前进程的直接子进程
- 获取当前进程的所有后代
前者是通过使用方法children()实现的,后者是通过使用方法descendants()实现的:
private static void infoOfChildProcess() throws IOException {
int childProcessCount = 5;
for (int i = 0; i < childProcessCount; i++) {
String javaCmd = ProcessUtils.getJavaCmd().getAbsolutePath();
ProcessBuilder processBuilder = new ProcessBuilder(javaCmd, "-version");
processBuilder.inheritIO().start();
}
Stream<ProcessHandle> children = ProcessHandle.current().children();
children.filter(ProcessHandle::isAlive)
.forEach(ph -> log.info("PID: {}, Cmd: {}", ph.pid(), ph.info().command()));
Stream<ProcessHandle> descendants = ProcessHandle.current()
.descendants();
descendants.filter(ProcessHandle::isAlive)
.forEach(ph -> log.info("PID: {}, Cmd: {}", ph.pid(), ph.info().command()));
}
6. 在进程终止时触发相关操作
我们可能希望在进程终止时运行一些东西,这可以通过使用java.lang.ProcessHandle接口中的onExit()方法来实现。该方法向我们返回一个CompletableFuture,它提供了在CompletableFuture完成时触发相关操作的能力。
在这里,CompletableFuture表示进程已经完成,但是进程是否成功完成并不重要。我们调用CompletableFuture的get()方法,等待它完成:
private static void infoOfExitCallback() throws IOException, InterruptedException, ExecutionException {
String javaCmd = ProcessUtils.getJavaCmd()
.getAbsolutePath();
ProcessBuilder processBuilder = new ProcessBuilder(javaCmd, "-version");
Process process = processBuilder.inheritIO()
.start();
ProcessHandle processHandle = process.toHandle();
log.info("PID: {} has started", processHandle.pid());
CompletableFuture onProcessExit = processHandle.onExit();
onProcessExit.get();
log.info("Alive: " + processHandle.isAlive());
onProcessExit.thenAccept(ph -> {
log.info("PID: {} has stopped", ph.pid());
});
}
onExit()方法在java.lang.Process接口中也可用。
7. 总结
在本教程中,我们介绍了Java 9中Process API的有趣新增功能,这些新增功能使我们能够更好地控制正在运行和生成的进程。
与往常一样,本教程的完整源代码可在GitHub上获得。