Ohhnews

分类导航

$ cd ..
Baeldung原文

Java程序如何获取自身进程ID

#java#进程id#processhandle#runtimemxbean#系统管理

[LOADING...]

1. 概述

获取当前 Java 进程的 ID 有助于监控、日志记录和系统管理。在 Java 9 之前,没有标准 API 可以完成此任务。此后,平台填补了这一空白。Java 9 引入了 ProcessHandle API。Java 10 在 RuntimeMXBean 中添加了 getPid() 方法。不过,许多较旧的方法在特定场景下仍然有用。

在本教程中,我们将探索多种获取当前 Java 进程 ID 的方法。

2. Java 9 之前的方法:解析 RuntimeMXBean 名称

使用 Java 8 或更早版本时,我们可以尝试使用 ManagementFactory.getRuntimeMXBean().getName() 来获取进程 ID。但是,我们得到的字符串格式应为 PID@hostname。在这种情况下,PID 就是 @ 符号之前的数字部分

那么,让我们看看如何从这样的字符串中提取 PID:

$ java
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
public class ProcessIdUtil {
    
    public static String getProcessIdPreJava9() {
        String runtimeName = ManagementFactory.getRuntimeMXBean().getName();
        int index = runtimeName.indexOf('@');
        if (index < 1) {
            return null;
        }
        return runtimeName.substring(0, index);
    }
}

这种方法有一个明显的限制。根据 Java 文档,返回的名称字符串可以是任意字符串。JVM 实现决定嵌入哪些信息。虽然大多数 JVM 都以这种格式包含 PID,但我们无法保证。这意味着解析方法可能会在某些 JVM 实现或未来版本中失效。

3. Java 9 及以上:ProcessHandle

Java 9 引入了 ProcessHandle 接口,作为 JEP 102(Java 进程 API)的一部分。它提供了一种直接、标准化的方式来访问本地进程 ID。此外,我们可以从 java.base 模块访问此接口,无需额外依赖。

ProcessHandle.current() 方法返回当前进程的句柄,其 pid() 方法返回本地进程 ID。

让我们看看实际操作:

$ java
import java.lang.ProcessHandle;
    public static long getProcessIdJava9Plus() {
        return ProcessHandle.current().pid();
    }

ProcessHandle API 返回类型安全的 long 值而不是字符串,并且是官方 Java 标准库的一部分。这意味着我们不需要任何外部依赖或特定于供应商的 hack。此外,该 API 设计为跨平台。

4. Java 10 及以上:RuntimeMXBean.getPid()

后来,Java 10 在 RuntimeMXBean 接口中添加了一个 getPid() 默认方法。RuntimeMXBean.getPid() 也直接以 long 值的形式返回进程 ID,无需任何字符串解析。

让我们看一个实际例子:

$ java
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
 
    public static long getProcessIdJava10Plus() {
        return ManagementFactory.getRuntimeMXBean().getPid();
    }

这里我们使用了最简洁的 JMX 方法之一。这种方法很方便,因为它使用了常见的 ManagementFactory API。它也是一个默认方法,因此在 JVM 上可以通过接口的默认实现获得

5. 创建版本感知工具类

在实际应用中,我们可以创建一个能够在不同 Java 版本上工作的统一工具方法。

让我们构建一个工具类,优先尝试最新的方法,并在需要时回退到较旧的方法

$ java
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
public class ProcessIdUtil {
    
    public static long getProcessId() {
        RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
        
        if (isJavaVersionOrGreater(10)) {
            return runtimeMxBean.getPid();
        }
        
        if (isJavaVersionOrGreater(9)) {
            return ProcessHandle.current().pid();
        }
        
        String runtimeName = runtimeMxBean.getName();
        int index = runtimeName.indexOf('@');
        if (index > 0) {
            return Long.parseLong(runtimeName.substring(0, index));
        }
        
        throw new IllegalStateException("Cannot determine process ID");
    }
    
    private static boolean isJavaVersionOrGreater(int version) {
        String javaVersion = System.getProperty("java.version");
        if (javaVersion.startsWith("1.")) {
            javaVersion = javaVersion.substring(2);
        }
        int majorVersion = Integer.parseInt(javaVersion.split("\\.")[0]);
        return majorVersion >= version;
    }
}

在这段代码中,我们展示了版本检测和优雅的回退。但是,在现代 Java 开发中,我们通常以特定的 Java 版本为目标。简而言之,版本感知方法在需要支持多个 Java 版本的库中最有用。

6. 总结

在本文中,我们探讨了在 Java 中获取进程 ID 的不同方法。具体来说,我们看到了 ManagementFactory.getRuntimeMXBean().getName() 方法通常适用于较旧的 Java 版本,但需要进行字符串解析。此外,我们还了解了 ProcessHandle.current().pid() 方法,这是 Java 9 中一个简单、类型安全的 API。接下来,我们尝试了 Java 10 中的 RuntimeMXBean.getPid()——另一个便捷选项。最后,我们创建了一个版本感知的方法,以平衡兼容性和现代方法。

与往常一样,所有源代码可以在 GitHub 上找到。