Java进程调用外部程序的几种方法

Java进程调用外部程序的几种方法

扫地生在之前有记录通过Java程序控制远程服务器进而调用指定程序的笔记。使用java代码连接到局域网内的Windows服务器中的软件并执行指令

现在在简单总结一下Java进程调用外部程序的几种方法,期间会简单的通过源码来看一下。

通过Java执行系统命令,与cmd中或Linux终端上一种执行Shell命令,最典型的用法就是使用Runtime,getRuntime().exec(command)或者new ProcessBuilder(cmdArray).start().

1 Runtime

Runtime类是Java程序的运行时环境。不能new出一个Runtime对象,只能通过getRuntime()方法获取当前Runtime运行时对象的引用。然后可以调用Runtime的方法查看和修改Java虚拟机的状态。

image-20220109213437020

通过上图可以看出,Runtime类并没有提供构造函数给我们,所以说我们是无法new一个Runtime对象的。这是简单单例,可以看到它的getRuntime()方法是没有做并行处理的,但整个环境中只允许出现一个Runtime对象。

Runtime和ProcessBuilder的不同点就是,启动子进程时的命令形式不同,Runtime.getRuntime.exec()可以把命令和参数写在一个String中,用空格分开,ProcessBuilder则是构造函数的参数中,传递一个由命令和参数组成的list或数组

Runtime类主要的方法如下:
image-20220109213733664

image-20220109213829693

  1. exec方法接收一个命令然后执行,通过该方法可以执行和cmd中用法一样命令。比如,Runtime.getRuntime().exec(“ls”),就和cmd中执行ls效果一样了。
  2. freeMemory()可以查看当前虚拟机内存中空闲内存还有多少。
  3. totalMemory()可以查看当前虚拟机使用的总内存大小。
  4. maxMemory()可以查看JVM的最终可以使用的最大内存是多少。
  5. availableProcessors()可以查看本机有多少处理器,即本机处理器是多少核。
  6. exit(int)方法可以退出当前Java程序的运行,System.exit(int)方法就是调用了Runtime.exit(int)方法来退出运行的。

以上这些在我们做分布式并行化编程查看计算机参数时也会使用到。

@Test
public void test2() throws IOException {
    Runtime.getRuntime().exec("java -version");
    System.out.println(Runtime.getRuntime().freeMemory());
    System.out.println(Runtime.getRuntime().totalMemory());
    System.out.println(Runtime.getRuntime().maxMemory());
}

image-20220109215651974

2 Process

Process是一个抽象类,主要方法如下:

image-20220109214513047

  1. waitFor()是让当前主进程等待这个process指向的子进程执行完成。
  2. exitValue()可以查看process指向的子进程执行完的退出值,0代表是正常运行结束。
  3. destroy()和destroyForcibly()可以终止process子进程的运行,后者是强制终止,前者与平台终止进程的具体实现有关。

通过Runtime.getRuntime().exe(...)可以创建一个本地进程执行传入的命令,这个方法返回Process的一个实例:

image-20220109214717818

process指向一个本地进程,相对于main进程来说,process指向的称为子进程。其中的is是为了获取子进程的输出信息。

明明是获取输出信息,为什么是InputStream呢?因为相对于main进程来说,子进程的输出就是main进程的输入,所以是InputStream。vice verse,如果要向子进程传递参数或者输入信息,则应该用OutputStream。但是不推荐用java 1.0引入的Process,而是用java 5.0的ProcessBuilder替代。

3 ProcessBuilder

ProcessBuilder是java 5.0引入的,start()方法返回Process的一个实例,如:

private static boolean processMp4(String oldfilepath) {
    if (!checkfile(inputPath)) {
        log.error(oldfilepath + "不是文件路径");
        return false;
    }
    List<String> command = new ArrayList<String>();
    command.add("ffmpeg");
    command.add("-i");
    command.add(oldfilepath);
    command.add("-c:v");
    command.add("libx264");
    command.add("-mbd");
    command.add("0");
    command.add("-c:a");
    command.add("aac");
    command.add("-strict");
    command.add("-2");
    command.add("-pix_fmt");
    command.add("yuv420p");
    command.add("-movflags");
    command.add("faststart");
    command.add(outputPath + "a.mp4");
    try {
        Process videoProcess = new ProcessBuilder(command).redirectErrorStream(true).start();
        new PrintStream(videoProcess.getErrorStream()).start();
        new PrintStream(videoProcess.getInputStream()).start();
        videoProcess.waitFor();
        return true;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

创建ProcessBuilder不需要通过Runtime,而Runtime.getRimtime().exec(string)正是调用了ProcessBuilder的构造方法来创建子进程并执行的。

ProcessBuilder的构造方法接收一个命令参数的数组形式,其中,第一个元素代表要执行的系统命令,后面的元素代表要传给该命令的参数

调用.start()方法运行之后,就可以获得该子进程的Process引用了,然后就可以调用Process的方法进行处理。

在用Runtime.getRuntime().exec()或ProcessBuilder(array).start()创建子进程Process之后,一定要及时取走子进程的输出信息和错误信息,否则输出信息流和错误信息流很可能因为信息太多导致被填满,最终导致子进程阻塞住,然后执行不下去。

比如上面扫地生的代码中使用线程及时取走了输出信息和错误信息:

new PrintStream(videoProcess.getErrorStream()).start();
/**
 * 在用Runtime.getRuntime().exec()或ProcessBuilder(array).start()创建子进程Process之后,
 * 一定要及时取走子进程的输出信息和错误信息,否则输出信息流和错误信息流很可能因为信息太多导致被填满,
 * 最终导致子进程阻塞住,然后执行不下去。
 */
class PrintStream extends Thread {
    InputStream is = null;
    public PrintStream(InputStream is)
    {
        this.is = is;
    }
    @Override
    public void run() {
        try{
            while(true) {
                int ch = is.read();
                if(ch != -1) {
                    System.out.print((char)ch);
                } else {
                    break;
                }
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }
}
posted @ 2022-01-09 22:07  技术扫地生—楼上老刘  阅读(1087)  评论(0编辑  收藏  举报