2. 使用 Process 类编译和运行 Java 程序
我们先来看一个例子:通过 Process
API 编译并运行另一个 Java 程序:
@Test
public void whenExecutedFromAnotherProgram_thenSourceProgramOutput3() throws IOException {
Process process = Runtime.getRuntime()
.exec("javac -cp src src\\main\\java\\com\\baeldung\\java9\\process\\OutputStreamExample.java");
process = Runtime.getRuntime()
.exec("java -cp src/main/java com.baeldung.java9.process.OutputStreamExample");
BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()));
int value = Integer.parseInt(output.readLine());
assertEquals(3, value);
}
这个功能在实际中用途广泛,比如自动化构建、代码执行引擎等场景。
3. 创建进程
Java 应用程序可以调用系统中的任意可执行程序(受操作系统权限限制)。
例如,使用 ProcessBuilder
来启动 Windows 的记事本:
ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
✅ 这种方式比直接调用 Runtime.getRuntime().exec()
更加灵活,支持设置环境变量、工作目录等。
4. 销毁进程
Process
类提供了销毁子进程的方法,但具体行为是平台相关的。
4.1. 通过引用销毁进程
ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
process.destroy(); // 尝试优雅关闭
4.2. 通过 PID 销毁进程
⚠️ 注意:这会终止不是由你创建的进程,请谨慎操作!
long pid = /* PID to kill */;
Optional<ProcessHandle> optionalProcessHandle = ProcessHandle.of(pid);
optionalProcessHandle.ifPresent(processHandle -> processHandle.destroy());
4.3. 强制销毁进程
当 destroy()
不生效时,可以使用 destroyForcibly()
:
ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
process.destroy();
if (process.isAlive()) {
process.destroyForcibly();
}
📌 建议顺序:先 destroy()
,再检查是否还活着,最后才考虑强制终止。
5. 等待进程完成
有两个重载方法用于等待进程结束。
5.1. waitFor()
阻塞当前线程直到子进程退出:
ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
assertThat(process.waitFor() >= 0);
5.2. waitFor(long timeout, TimeUnit unit)
带超时机制的版本:
ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
assertFalse(process.waitFor(1, TimeUnit.SECONDS));
返回值说明:
true
: 子进程已退出false
: 超时退出
6. 获取进程退出码:exitValue()
该方法不会阻塞,如果进程未结束会抛出 IllegalThreadStateException
。否则返回进程的退出码(通常为非负整数):
@Test
public void
givenSubProcess_whenCurrentThreadWillNotWaitIndefinitelyforSubProcessToEnd_thenProcessExitValueReturnsGrt0()
throws IOException {
ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
assertThat(process.exitValue() >= 0);
}
❌ 注意:不要在进程未结束前调用,否则会抛异常。
7. 判断进程是否存活:isAlive()
快速判断进程是否仍在运行:
ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
Thread.sleep(10000);
process.destroy();
assertTrue(process.isAlive());
8. 处理进程流
默认情况下,子进程没有自己的控制台,其标准输入/输出/错误会被重定向到父进程。
8.1. getErrorStream()
获取错误输出流:
@Test
public void givenSubProcess_whenEncounterError_thenErrorStreamNotNull() throws IOException {
Process process = Runtime.getRuntime().exec(
"javac -cp src src\\main\\java\\com\\baeldung\\java9\\process\\ProcessCompilationError.java");
BufferedReader error = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String errorString = error.readLine();
assertNotNull(errorString);
}
8.2. getInputStream()
获取标准输出流:
@Test
public void givenSourceProgram_whenReadingInputStream_thenFirstLineEquals3() throws IOException {
Process process = Runtime.getRuntime().exec(
"javac -cp src src\\main\\java\\com\\baeldung\\java9\\process\\OutputStreamExample.java");
process = Runtime.getRuntime()
.exec("java -cp src/main/java com.baeldung.java9.process.OutputStreamExample");
BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()));
int value = Integer.parseInt(output.readLine());
assertEquals(3, value);
}
8.3. getOutputStream()
向子进程发送输入数据:
Writer w = new OutputStreamWriter(process.getOutputStream(), "UTF-8");
w.write("send to child\n");
8.4. 过滤进程流
可以根据条件筛选运行中的进程:
@Test
public void givenRunningProcesses_whenFilterOnProcessIdRange_thenGetSelectedProcessPid() {
assertThat(((int) ProcessHandle.allProcesses()
.filter(ph -> (ph.pid() > 10000 && ph.pid() < 50000))
.count()) > 0);
}
9. Java 9 对 Process API 的增强
Java 9 引入了更丰富的 API 来管理和监控进程。
9.1. 获取当前进程信息
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());
}
示例输出:
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]
9.2. 获取新创建进程的信息
String javaCmd = ProcessUtils.getJavaCmd().getAbsolutePath();
ProcessBuilder processBuilder = new ProcessBuilder(javaCmd, "-version");
Process process = processBuilder.inheritIO().start();
ProcessHandle processHandle = process.toHandle();
9.3. 枚举系统中所有可见进程
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());
});
}
9.4. 枚举子进程或后代进程
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()));
}
9.5. 在进程终止时触发回调
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()
返回的是 CompletableFuture<ProcessHandle>
,可以链式处理后续逻辑。
10. 总结
本文详细介绍了 Java 中的 Process
API,包括基本操作如创建、销毁、等待进程结束,以及对进程流的处理。同时讲解了 Java 9 中新增的强大功能,如获取进程信息、枚举系统进程、监听进程退出等。
这些 API 在自动化部署、容器化管理、脚本执行等高级场景中有广泛应用。
源码见 GitHub。