位于上海,服务全国!

位于上海,服务全国!

理解Java进程和Java ProcessBuilder

作者:admin 分类: 时间:2017-06-21 16:17:46 点击量:2673

Java的扩展API支持,不仅可以处理轻量级进程(线程),还可以处理由于其内存和资源占用而引起的,被称为重量级进程的支持。 java.lang包中定义的进程和其他相关类的类在这方面提供了重要的支持,并且通常应用了该进程的许多核心功能。 这些类主要用于收集关于并实现运行时进程的信息。 本文介绍了这些API的一些常见功能,以了解Java中的进程。


运行环境类
每个Java程序都有一个运行环境类的实例,它封装了程序的运行时环境。 这个类不能被实例化,但是借助于静态方法Runtime.getRuntime(),我们可以获得当前运行的程序的运行环境。 在运行环境类中定义了几种方法。 可以调用这些方法来获取有关运行时环境的信息,例如可用于JVM的可用处理器数量,可用内存,加载本机库,显式调用垃圾回收器等。

以下是一个简单例子。
package org.mano.example;

public class ProcessDemo {

   public static void main(String[] args) throws Exception {

      Runtime r=Runtime.getRuntime();

      System.out.println("No of Processor: "+
         r.availableProcessors());
      System.out.println("Total memory: "+r.totalMemory());
      System.out.println("Free memory: "+r.freeMemory());
      System.out.println("Memory occupied: "+
         (r.totalMemory()-r.freeMemory()));

      for(int i=0;i<=10000;i++){
         new Object();
      }

      r.gc();

      System.out.println("::Memory status::");
      System.out.println("Total memory: "+r.totalMemory());
      System.out.println("Free memory: "+r.freeMemory());
      System.out.println("Memory occupied: "+
         (r.totalMemory()-r.freeMemory()));
   }
}

除了这些外,还有一个重载的exec的方法,它返回对进程实例的引用。 重载的exec方法如下:

    Process exec(String command)
    Process exec(String command, String[] envp )
    Process exec(String command, String[] envp, File dir )
    Process exec(String[] cmdarray)
    Process exec(String[] cmdarray, String[] envp )
    Process exec(String[] cmdarray, String[] envp, File dir )

注意,命令参数表示我们要在单独进程中执行的命令,并且在其一个数组形式的变量中,我们可以指定该命令,以及指定在单独进程中命令传递的参数。 envp参数指定命令使用的环境变量,File参数表示工作目录。
这个方法可以用来执行一个单独的进程,我们将在下面看到。
进程类


该进程是java.lang包中定义的一个抽象类,其用于封装正在执行的程序运行环境信息。由运行环境实例调用的exec方法返回该类实例的引用。另一种方式是通过ProcessBuilder.start()方法创建此类的实例。
由进程类定义的方法可用于由进程执行的输入/输出操作,检查进程的存在状态,等待进程完成并终止。然而,这些方法并不构建为在本地平台的特殊进程上工作,如守护进程,shell脚本等。
有趣的是,由exec方法创建的进程不拥有控制台。因此,排除了其在标准文件中的输入,输出和错误的重定向。相反,它将(stdin,stdout,stderr)重定向到父进程。如果需要,我们可以通过使用类中定义的方法获取流来访问它们,例如getInputStream(),getOutputStream()和getErrorSteam())。
这些是我们可以向子进程输入信息并获取结果的方式。但是,有一个问题:标准输入和输出缓冲区的大小有限制,并由底层平台定义。除非标准输入和输出流分别及时地写入和读取子进程,否则可能会阻塞或死锁子进程。

这个类中定义的一些常见方法有:

    Exits code returned from the process executed
        int exitValue()
    Reads/writes output, error, and input streams to and from the process.
        InputStream getErrorStream()
        InputStream getInputStream()
        OutputStream getOutputStream()
    Checks to see if the invoking process is still running.
        Boolean isAlive()
等待调用进程结束。 该方法返回的整数值是进程的退出代码。 在另一个重载方法中,我们可以指定等待时间。 如果进程已经终止,则该方法返回true,如果发生超时,则返回false。

        int waitFor()
        Boolean waitFor(long timeOut, TimeUnit unit)
 这两种方法用于销毁或终止进程。第 一个,第二个,都是强制的。
        void destroy()
        Process destroyForcibly()
让我们编写一个简单的Java程序以打开应用程序作为一个单独的进程。 打开后,程序会等待10秒钟,然后摧毁进程,这将立即关闭应用程序。
package org.mano.example;

import java.util.concurrent.TimeUnit;

public class ProcessDemo {

   public static void main(String[] args) throws Exception {

      Runtime r = Runtime.getRuntime();
      Process p = r.exec("firefox");
      p.waitFor(10, TimeUnit.SECONDS);
      p.destroy();
   }
}
ProcessBuilder类
这是Process的一个辅助类,并被实例化以管理进程属性的集合。 我们可以使用ProcessBuilder类实例定义的属性来调用start方法以创建一个新进程。 对start方法的重复调用将创建一个具有相同属性的新进程。 注意,ProcessBuilder并是一个同步类; 因此,如果没有明确同步,则不能安全地通过多个线程访问此类的实例。 从Java 1.5开始,ProcessBuilder.start()是创建进程的首选方式。
ProcessBuilder类定义了两个构造函数,如:

ProcessBuilder(List<String> command)
ProcessBuilder(String... command)

传递给两个构造函数的参数意义相同。 在第一个构造函数中,要被执行的命令和命令行参数一起被传递到一个字符串列表中。 而在第二个构造函数中,命令和命令行参数是通过varargs参数来指定的。 我们可以使用任何一个构造函数,且取决于传递参数的方式。
这是一个关于我们如何运行Linux命令的例子,例如cal 2022,捕获输入流中的输出,并显示它。
package org.mano.example;

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class ProcessDemo {

   public static void main(String[] args) {

      System.out.println
         ("*************Calendar for Year**********");
      try {
         ProcessBuilder pb = new
            ProcessBuilder("cal", "2022");
         final Process p=pb.start();
         BufferedReader br=new BufferedReader(
            new InputStreamReader(
               p.getInputStream()));
            String line;
            while((line=br.readLine())!=null){
               System.out.println(line);
            }
      } catch (Exception ex) {
         System.out.println(ex);
      }
      System.out.println
         ("************************************");
   }
}
可以修改之前的程序以重定向命令输出到一个文件。 请注意,其可以通过几种方式完成。 以下是一种方法,且没有改变之前的太多代码。
package org.mano.example;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.InputStreamReader;

public class ProcessDemo {

   public static void main(String[] args) {
      try {
         ProcessBuilder pb = new
            ProcessBuilder("cal", "2022");
         final Process p=pb.start();
         BufferedReader br=new BufferedReader(
            new InputStreamReader(
               p.getInputStream()));
               BufferedWriter bw=new BufferedWriter(
                  new FileWriter(new File("mycal2022.txt")));
                  String line;
                  while((line=br.readLine())!=null){
                     bw.write(line);
                  }
                  bw.close();
      } catch (Exception ex) {
         System.out.println(ex);
      }
   }
}

Java不提供直接处理通道命令的方法,例如:
<pre'>echo 'scale=24; 22/7' | bc

指定为“|”的通道实际上由shell解释为两个进程之间的通信方式。 Java没有如何解释它的线索。 但是,我们可以调用shell并将piped命令作为命令行参数发送给它。 这种技术更多被黑客应用而不是Java真正支持的类型。 顺便说一句,有一些迂回和复杂的方式来实现类似的效果,例如通过使用输入和输出流的重定向。 而现在我们不必深入研究这些复杂性问题。
因此,我们可以通过调用shell来修改一些上面的代码,并运行管道命令,如下所示。
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.InputStreamReader;

public class ProcessDemo {

   public static void main(String[] args) {

      try {
         ProcessBuilder pb = new
            ProcessBuilder("/bin/sh", "-c",
         "echo 'scale=24;22/7' | bc");
         final Process p=pb.start();
         BufferedReader br=new BufferedReader(
            new InputStreamReader(
               p.getInputStream()));
               String line;
               while((line=br.readLine())!=null){
                  System.out.println(line);
               }
      } catch (Exception ex) {
         System.out.println(ex);
      }
   }
}

结论
提供一个抽象的静态类,被称为ProcessBuilder.Redirect,并与ProcessBuilder类相关联。 该类表示子进程的源和目标I / O的链接。
 I / O可以通过to()方法重定向到文件,或使用from()方法的文件,或者使用appendTo()方法附加到文件。 创建一个进程很简单:通过命令和参数列表创建一个ProcessBuilder的实例,并调用该实例上的start()方法。