7.2.2 Runtime类与Java9的ProcessHandle
Runtime
类代表Java
程序的运行时环境,每个Java
程序都有一个与之对应的Runtime
实例,应用程序通过该对象与其运行时环境相连。应用程序不能创建自己的Runtime
实例,但可以通过getRuntime()
方法获取与之关联的Runtime
对象。
垃圾回收方法
与System
类似的是, Runtime
类也
- 提供
gc()
方法来通知系统进行垃圾回收方法
- 提供
runFinalization()
方法用来通知系统调用finalize()方法来进行资源清理
。
加载文件和加载动态链接库方法
Runtime
类提供load(String fileName)
和loadLibrary(String libName)
方法来加载文件
和加载动态链接库
。
访问JVM相关信息
Runtime
类代表Java
程序的运行时环境,可以访问JVM
的相关信息,如处理器数量
、内存信息
等
1 2 3 4 5 6 7 8 9 10
| public class RuntimeTest { public static void main(String[] args) { Runtime rt = Runtime.getRuntime(); System.out.println("处理器数量:" + rt.availableProcessors()); System.out.println("空闲内存数:" + rt.freeMemory()); System.out.println("总内存数:" + rt.totalMemory()); System.out.println("可用最大内存数:" + rt.maxMemory()); } }
|
运行结果:
1 2 3 4
| 处理器数量:4 空闲内存数:126927264 总内存数:128974848 可用最大内存数:1884815360
|
启动进程运行命令
Runtime
类还可以直接单独启动一个进程来运行操作系统的命令
。Runtime
提供了一系列exec()
方法来运行操作系统命令。
实例
启动Windows
系统里的”记事本”程序。
1 2 3 4 5 6 7
| public class ExecTest { public static void main(String[] args) throws Exception { Runtime rt = Runtime.getRuntime(); rt.exec("notepad.exe"); } }
|
运行效果:记事本被打开
。
获取exec
启动的进程信息
获取Process对象
通过exec
启动平台上的命令之后,它就变成了一个进程,java
中Process
来代表进程,可以使用Process
来接收exec
方法的返回值。
Java9
还新增了一个ProcessHandle
接口,通过该接口可获取进程的ID
、父进程
和后代进程
;通过该接口的onExit()
方法可在进程结束时完成某些行为.
ProcessHandle
还提供了一个ProcessHandle.Info
内部类,用于获取进程的命令、参数、启动时间、累计运行时间、用户等信息。
如何获取ProcessHandle对象
通过Process
对象的方法toHandle()
可以取得ProcessHandle
对象,然后就可以通过ProcessHandle
对象来获取进程相关信息。
下面程序示范了通过ProcessHandle
获取进程的相关信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import java.util.concurrent.*;
public class ProcessHandleTest { public static void main(String[] args) throws Exception { Runtime rt = Runtime.getRuntime(); Process p = rt.exec("notepad.exe"); ProcessHandle ph = p.toHandle(); System.out.println("进程是否运行: " + ph.isAlive()); System.out.println("进程ID: " + ph.pid()); System.out.println("父进程: " + ph.parent()); ProcessHandle.Info info = ph.info(); System.out.println("进程命令: " + info.command()); System.out.println("进程参数: " + info.arguments()); System.out.println("进程启动时间: " + info.startInstant()); System.out.println("进程累计运行时间: " + info.totalCpuDuration()); CompletableFuture<ProcessHandle> cf = ph.onExit(); cf.thenRunAsync(() -> { System.out.println("程序退出"); }); Thread.sleep(5000); } }
|
实例 执行进程并返回进程的输出
被调用的进程
下面创建一个PrintArgs
程序,用来给其他进程调用.
1 2 3 4 5 6 7 8 9 10 11 12 13
| package system.test;
public class PrintArgs { public static void main(String args[]) { String simpleName = PrintArgs.class.getSimpleName(); System.out.println("----- " + simpleName + " start -----------------------------------"); for (int i = 0; i < args.length; i++) { System.out.println("[args-" + i + "]:" + args[i]); } System.out.println("----- " + simpleName + " end -----------------------------------"); } }
|
编译 打包 测试
先在当前目录下编译这个java
文件:
1
| E:\dev2\idea_workspace\Test\src\system\test>javac -d . -encoding utf-8 PrintArgs.java
|
然后打包成可执行jar包:
1 2 3 4 5
| E:\dev2\idea_workspace\Test\src\system\test>jar cvfe PrintArgs.jar system.test.PrintArgs system 已添加清单 正在添加: system/(输入 = 0) (输出 = 0)(存储了 0%) 正在添加: system/test/(输入 = 0) (输出 = 0)(存储了 0%) 正在添加: system/test/PrintArgs.class(输入 = 1094) (输出 = 620)(压缩了 43%)
|
接着,查看jar包是否打包正确
1 2 3 4 5 6
| E:\dev2\idea_workspace\Test\src\system\test>jar tf PrintArgs.jar META-INF/ META-INF/MANIFEST.MF system/ system/test/ system/test/PrintArgs.class
|
最后,运行这个可执行jar包
1 2 3 4 5 6 7 8
| E:\dev2\idea_workspace\Test\src\system\test>java -jar PrintArgs.jar 1 2 3 ----- PrintArgs start ----------------------------------- 工作目录:E:\dev2\idea_workspace\Test\src\system\test [args-0]:1 [args-1]:2 [args-2]:3 ----- PrintArgs end -----------------------------------
|
调用进程
可以看到通过命令java -jar PrintArgs.jar 1 2 3
可以正确运行这个jar
包,接下来就是,通过Process
来执行这个命令。
创建Process
有如下两种方式:
- 通过
Runtime.exec("cmd命令")
来运行一个进程.
- 通过
ProcessBuilder
对象的start
方法来运行一个进程.
现在
工具类 运行进程并返回进程的标准输出
注意获取输出的时候要注意控制台的编码,我的控制台默认编码是gbk
.如果是其他编码则需要指定编码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| package system.test;
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader;
public class ProcessRunner {
public String runProcess(String commandStr) { return runProcess(commandStr, "gbk"); }
public String runProcess(String commandStr, String cmdEncoding) { BufferedReader br = null; StringBuilder sb = new StringBuilder(); try { Process p = Runtime.getRuntime().exec(commandStr); br = new BufferedReader(new InputStreamReader(p.getInputStream(), cmdEncoding)); String line; while ((line = br.readLine()) != null) { sb.append(line.concat("\n")); } int exitCode = p.waitFor(); if (exitCode != 0) return null;
} catch (Exception e) { e.printStackTrace(); } finally { if (br != null) { try { br.close(); } catch (Exception e) { e.printStackTrace(); } } } return sb.toString(); }
public String runProcess(ProcessBuilder processBuilder) { return runProcess(processBuilder, "gbk"); }
public String runProcess(ProcessBuilder processBuilder, String cmdEncoding) { Process process = null; StringBuffer sb = new StringBuffer(); try { process = processBuilder.start(); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), cmdEncoding)); String line; while ((line = reader.readLine()) != null) { sb.append(line + "\n"); } int exitCode = process.waitFor(); reader.close(); if (exitCode != 0) return null;
} catch (IOException | InterruptedException e) { e.printStackTrace(); } return sb.toString(); } }
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| package system.test;
import java.io.*; import java.util.ArrayList; import java.util.List;
public class ProcessTest { public static void main(String[] args) { testRuntime();
testProcessBuilder(); }
private static void testRuntime() { String jarPath = "E:\\dev2\\idea_workspace\\Test\\src\\system\\test\\PrintArgs.jar"; System.out.println(new ProcessRunner().runProcess("java -jar " + jarPath + " 1 2 3")); }
private static void testProcessBuilder() { List<String> command = new ArrayList<>(); command.add("java"); command.add("-jar"); command.add("PrintArgs.jar"); command.add("1"); command.add("2"); command.add("3"); ProcessBuilder processBuilder = new ProcessBuilder(command); processBuilder.directory(new File("E:\\dev2\\idea_workspace\\Test\\src\\system\\test")); String processOutput = new ProcessRunner().runProcess(processBuilder); System.out.println(processOutput); } }
|
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12
| ----- PrintArgs start ----------------------------------- 工作目录:E:\dev2\idea_workspace\Test [args-0]:1 [args-1]:2 [args-2]:3 ----- PrintArgs end ----------------------------------- ----- PrintArgs start ----------------------------------- 工作目录:E:\dev2\idea_workspace\Test\src\system\test [args-0]:1 [args-1]:2 [args-2]:3 ----- PrintArgs end -----------------------------------
|
Runtime方式和ProcessBuilder方式的对比
ProcessBuilder设置命令比较方便
Runtime
方式需要把要指定的cmd
命令,全部写在一个字符串里,如果命令比较长则容易出现错误.
ProcessBuilder
方式设置命令比较简单,命令写在List
中或者字符串变参中,写起来比较方便,不容易出现错误.
ProcessBuilder可以指定工作目录
Runtime
方式的工作目录默认与父进程相同,运行程序时,一般需要输入程序的绝对路径,比较繁琐.
ProcessBuilder
方式可以指定工作目录,这样可执行程序的路径可以使用相对路径
JDK1.5
之后推荐使用ProcessBuilder
方式来运行进程