java.lang.Runtime.exec的左膀右臂:流输入和流读取详解

 更新时间:2021年11月03日 11:45:05   作者:Morpheus丶  
这篇文章主要介绍了java.lang.Runtime.exec的左膀右臂:流输入和流读取详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

在java.lang.Runtime.exec的使用中,我们经常会用到将重定向命令执行的输入/结果或者将错误信息读取出来.

那么,在使用过程中,我们如何正确的使用呢?

什么是java.lang.Runtime

首先我们要明确一点,什么是Java.lang.Runtime? 我们来看官方[->link<-]的描述:

" Every Java application has a single instance of class Runtime that allows the application to interface with the environment in which the application is running. The current runtime can be obtained from the getRuntime method.

An application cannot create its own instance of this class. "

也就是说,Runtime是每个java-application运行时有且仅有一个的当前实例.允许Application接入当前运行环境.

我们再来看看Runtime的exec()方法:

" Executes the specified command in a separate process. "

这个方法可以让我们在一个进程中执行指定的命令.其返回类型是Process类.

那么我们还需要来看一下Process类:

什么是java.lang.Process

什么是Java.lang.Process? 我们来看官方[->link<-]的描述:

"The class Process provides methods for performing input from the process, performing output to the process, waiting for the process to complete, checking the exit status of the process, and destroying (killing) the process."

也就是说这个类提供控制线程的方法.

我们再来看看Process提供的获取输入流和输出流的方法:

public abstract InputStream getInputStream()

"Returns the input stream connected to the normal output of the subprocess.
The stream obtains data piped from the standard output of the process represented by this Process object."
public abstract OutputStream getOutputStream()
"Returns the output stream connected to the normal input of the subprocess.
Output to the stream is piped into the standard input of the process represented by this Process object."

public abstract InputStream getErrorStream()

"Returns the input stream connected to the error output of the subprocess.
The stream obtains data piped from the error output of the process represented by this Process object."

到这里,我们就明白里其中的因果>从exec()返回的Process的对象中调用获取流的方法.从而达到目的!

具体做法

在需要使用exec()去执行命令并获取返回值的时候,具体的做法是什么呢?

仅仅围绕这个思路:"从exec()返回的Process的对象中调用获取流的方法getXStream"

String s = null;
              Process p = Runtime
                      .getRuntime()
                      .exec(
                              new String[]{"/bin/sh",
                                          "-c",
                                          "java HelloWorld"},null,dir);
 
              BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
              BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
 
              //打印出输出结果
              log.info("标准输出命令");
              while ((s = stdInput.readLine()) != null) {
                    log.info(s);
              }
 
              log.info("标准错误的输出命令");
              while ((s = stdError.readLine()) != null) {
                    log.info(s);
              }

其中

dir 是我的HelloWorld.class的存放目录,只能这样用,请参照这篇

HelloWorld.java 的内容是Sysotem.out.println打印几行Hello World,此处没有编译,使用时应注意.

到此,大功告成!

Runtime.exec 陷阱

该类java.lang.Runtime具有一个称为的静态方法getRuntime(),该方法检索当前的Java Runtime Environment。这是获得对该Runtime对象的引用的唯一方法。使用该参考,您可以通过调用Runtime类的exec()方法来运行外部程序。开发人员经常调用此方法来启动浏览器,以显示HTML的帮助页面。

该exec()命令有四个重载版本:

public Process exec(String command);
public Process exec(String [] cmdArray);
public Process exec(String command, String [] envp);
public Process exec(String [] cmdArray, String [] envp);

对于这些方法中的每一个,命令(可能还有一组参数)都传递给特定于操作系统的函数调用。随后,这将参考Process返回给Java VM的类来创建特定于操作系统的进程(正在运行的程序)。所述Process类是一个抽象类,因为一个特定的子类Process存在于每个操作系统。

您可以将三个可能的输入参数传递给这些方法:

  • 一个字符串,代表要执行的程序和该程序的所有参数
  • 字符串数组,用于将程序与其参数分开
  • 一组环境变量

以形式传递环境变量name=value。如果exec()对程序及其参数使用单个字符串的版本,请注意,通过StringTokenizer类使用空格作为分隔符来解析该字符串。

IllegalThreadStateException

要执行Java VM外部的进程,我们使用exec()方法。要查看外部进程返回的值,我们exitValue()。

如果外部过程尚未完成,则该exitValue()方法将抛出IllegalThreadStateException;。这就是该程序失败的原因。尽管文档中说明了这一事实,但为什么不能等到此方法可以给出有效答案呢?

对该Process类中可用的方法进行更彻底的研究,就会发现waitFor()可以做到这一点的方法。实际上,waitFor()还会返回退出值,这意味着您将不会使用exitValue()和waitFor()彼此结合,而是会选择一个或另一个。你会使用的唯一可能的时间exitValue(),而不是waitFor()会当你不希望你的程序阻止等待外部过程中可能永远不会完成。与其使用该waitFor()方法,不如将一个被调用的布尔参数waitFor传入该exitValue()方法以确定当前线程是否应等待。布尔值会更有利,因为exitValue()是此方法的更合适的名称,两个方法在不同条件下不必执行相同的功能。这种简单的条件判别是输入参数的领域。

import java.util.*;
import java.io.*;
public class BadExecJavac
{
    public static void main(String args[])
    {
        try
        {            
            Runtime rt = Runtime.getRuntime();
            Process proc = rt.exec("javac");
            int exitVal = proc.exitValue();
            System.out.println("Process exitValue: " + exitVal);
        } catch (Throwable t)
          {
            t.printStackTrace();
          }
    }
}
import java.util.*;
import java.io.*;
public class BadExecJavac2
{
    public static void main(String args[])
    {
        try
        {            
            Runtime rt = Runtime.getRuntime();
            Process proc = rt.exec("javac");
            int exitVal = proc.waitFor();
            System.out.println("Process exitValue: " + exitVal);
        } catch (Throwable t)
          {
            t.printStackTrace();
          }
    }
}

因此,为避免此陷阱,请捕获IllegalThreadStateException或等待该过程完成。

为什么Runtime.exec()挂起

由于某些本机平台仅为标准输入和输出流提供有限的缓冲区大小,因此未能及时写入子流程的输入流或读取子流程的输出流可能导致子流程阻塞,甚至死锁。

现在,让我们关注JDK文档并处理javac过程的输出。当您javac不带任何参数运行时,它会生成一组用法语句,这些用法语句描述了如何运行程序以及所有可用程序选项的含义。知道这将stderr流到流,您可以轻松地编写一个程序以在等待进程退出之前耗尽该流。清单4.3完成了该任务。尽管此方法行之有效,但这不是一个好的通用解决方案。因此,清单4.3的程序被命名为MediocreExecJavac;。它仅提供平庸的解决方案。更好的解决方案将同时清空标准错误流和标准输出流。最好的解决方案是同时清空这些流(稍后再说明)

import java.util.*;
import java.io.*;
public class MediocreExecJavac
{
    public static void main(String args[])
    {
        try
        {            
            Runtime rt = Runtime.getRuntime();
            Process proc = rt.exec("javac");
            InputStream stderr = proc.getErrorStream();
            InputStreamReader isr = new InputStreamReader(stderr);
            BufferedReader br = new BufferedReader(isr);
            String line = null;
            System.out.println("<ERROR>");
            while ( (line = br.readLine()) != null)
                System.out.println(line);
            System.out.println("</ERROR>");
            int exitVal = proc.waitFor();
            System.out.println("Process exitValue: " + exitVal);
        } catch (Throwable t)
          {
            t.printStackTrace();
          }
    }
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Spring @Scheduler使用cron表达式时的执行问题详解

    Spring @Scheduler使用cron表达式时的执行问题详解

    Spring给程序猿们带来了许多便利。下面这篇文章主要给大家介绍了关于Spring @Scheduler使用cron表达式时的执行问题的相关资料,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2018-09-09
  • 图文讲解Java中实现quickSort快速排序算法的方法

    图文讲解Java中实现quickSort快速排序算法的方法

    这篇文章主要介绍了Java中实现quickSort快速排序算法的方法,文章最后还介绍了一种单向扫描的实现方法,需要的朋友可以参考下
    2016-05-05
  • 如何解决Maven打包时每次都出现Download maven-metadata.xml卡住问题

    如何解决Maven打包时每次都出现Download maven-metadata.xml卡住问题

    这篇文章主要介绍了如何解决Maven打包时每次都出现Download maven-metadata.xml卡住问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-05-05
  • 基于bufferedreader的read()与readline()读取出错原因及解决

    基于bufferedreader的read()与readline()读取出错原因及解决

    这篇文章主要介绍了bufferedreader的read()与readline()读取出错原因及解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Java线程变量ThreadLocal详细解读

    Java线程变量ThreadLocal详细解读

    这篇文章主要介绍了Java线程变量ThreadLocal详细解读,多线程访问同一个变量的时候,很容易出现问题,特别是多线程对一个共享变量进行写入的时候,为了线程的安全在进行数据写入时候会进行数据的同步,需要的朋友可以参考下
    2024-01-01
  • Spring Data JPA 复杂/多条件组合分页查询

    Spring Data JPA 复杂/多条件组合分页查询

    本文主要介绍了Spring Data JPA 复杂/多条件组合分页查询的相关资料。具有很好的参考价值。下面跟着小编一起来看下吧
    2017-04-04
  • Java实现redis分布式锁的三种方式

    Java实现redis分布式锁的三种方式

    本文主要介绍了Java实现redis分布式锁的三种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • Java并发编程之ReadWriteLock读写锁的操作方法

    Java并发编程之ReadWriteLock读写锁的操作方法

    这篇文章主要介绍了Java并发编程之ReadWriteLock读写锁的操作方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-02-02
  • Maven引入外部jar的几种方法(小结)

    Maven引入外部jar的几种方法(小结)

    这篇文章主要介绍了Maven引入外部jar的几种方法(小结),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • 基于Java的度分秒坐标转纯经纬度坐标的漂亮国基地信息管理的方法

    基于Java的度分秒坐标转纯经纬度坐标的漂亮国基地信息管理的方法

    本文以java语言为例,详细介绍如何管理漂亮国的基地信息,为下一步全球的空间可视化打下坚实的基础,首先介绍如何对数据进行去重处理,然后介绍在java当中如何进行度分秒位置的转换,最后结合实现原型进行详细的说明,感兴趣的朋友跟随小编一起看看吧
    2024-06-06

最新评论