服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - java.lang.Runtime.exec的左膀右臂:流输入和流读取详解

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

2022-03-10 00:40Morpheus丶 Java教程

这篇文章主要介绍了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,此处没有编译,使用时应注意.

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

到此,大功告成!

 

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();
        }
  }
}

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

原文链接:https://blog.csdn.net/timo1160139211/article/details/75050886

延伸 · 阅读

精彩推荐
  • Java教程shiro之记住登录信息

    shiro之记住登录信息

    Shiro提供了记住我(RememberMe)的功能,当关闭浏览器时下次再次打开还能记住你的信息,下面小编给大家分享shiro之记住登录信息的相关知识,感兴趣的朋...

    动力节点6182021-01-07
  • Java教程java数组排列组合问题汇总

    java数组排列组合问题汇总

    这篇文章主要为大家详细汇总了java数组排列组合问题,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    xh1511162021-03-31
  • Java教程Spring Boot 五种热部署方式,极速开发就是生产力!

    Spring Boot 五种热部署方式,极速开发就是生产力!

    在 Spring Boot 中,模板引擎的页面默认是开启缓存的,如果修改了页面的内容,则刷新页面是得不到修改后的页面的,因此我们可以在application.properties中关...

    民工哥技术之路6712021-12-31
  • Java教程Spring Cloud引入Eureka组件,完善服务治理

    Spring Cloud引入Eureka组件,完善服务治理

    这篇文章主要介绍了Spring Cloud引入Eureka组件,完善服务治理的过程详解,帮助大家更好的理解和使用spring cloud,感兴趣的朋友可以了解下...

    精灵王11282021-08-04
  • Java教程Java使用Math.random()结合蒙特卡洛方法计算pi值示例

    Java使用Math.random()结合蒙特卡洛方法计算pi值示例

    这篇文章主要介绍了Java使用Math.random()结合蒙特卡洛方法计算pi值的方法,简单说明了结合具体实例蒙特卡洛方法的原理,并结合具体实例形式分析了java使用蒙...

    软货11032021-01-04
  • Java教程Java设计模式之java迭代器模式详解

    Java设计模式之java迭代器模式详解

    这篇文章主要介绍了java迭代器模式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一...

    大忽悠爱忽悠8122022-01-04
  • Java教程SpringMVC文件上传的配置实例详解

    SpringMVC文件上传的配置实例详解

    本文通过实例代码给大家介绍SpringMVC文件上传的配置相关内容,本文介绍的非常详细,具有参考借鉴价值,感兴趣的朋友一起学习吧 ...

    逆火狂飙2592020-04-28
  • Java教程Java算法实现杨辉三角的讲解

    Java算法实现杨辉三角的讲解

    今天小编就为大家分享一篇关于Java算法实现杨辉三角的讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来...

    kuls4202021-07-02