浅析ProcessBuilder
歡迎支持筆者新作:《深入理解Kafka:核心設計與實踐原理》和《RabbitMQ實戰指南》,同時歡迎關注筆者的微信公眾號:朱小廝的博客。
 
歡迎跳轉到本文的原文鏈接:https://honeypps.com/java/process-builder-quick-start/
概述
ProcessBuilder類是J2SE 1.5在java.lang中新添加的一個新類,此類用于創建操作系統進程,它提供一種啟動和管理進程(也就是應用程序)的方法。在J2SE 1.5之前,都是由Process類處理實現進程的控制管理。每個 ProcessBuilder 實例管理一個進程屬性集。它的start() 方法利用這些屬性創建一個新的 Process 實例。start() 方法可以從同一實例重復調用,以利用相同的或相關的屬性創建新的子進程。
每個進程生成器(即ProcessBuilder對象)管理這些進程屬性:
 命令 command
 是一個字符串列表,它表示要調用的外部程序文件及其參數(如果有)。在此,表示有效的操作系統命令的字符串列表是依賴于系統的。例如,每一個總體變量,通常都要成為此列表中的元素,但有一些操作系統,希望程序能自己標記命令行字符串——在這種系統中,Java 實現可能需要命令確切地包含這兩個元素。
環境 environment
 是從變量 到值 的依賴于系統的映射。初始值是當前進程環境的一個副本(請參閱 System.getenv())。
工作目錄 working directory
 默認值是當前進程的當前工作目錄,通常根據系統屬性 user.dir 來命名。
redirectErrorStream屬性
 最初,此屬性為 false,意思是子進程的標準輸出和錯誤輸出被發送給兩個獨立的流,這些流可以通過 Process.getInputStream() 和 Process.getErrorStream() 方法來訪問。如果將值設置為 true,標準錯誤將與標準輸出合并。這使得關聯錯誤消息和相應的輸出變得更容易。在此情況下,合并的數據可從 Process.getInputStream() 返回的流讀取,而從 Process.getErrorStream() 返回的流讀取將直接到達文件尾。
既然有Process類,那為什么還要發明個ProcessBuilder類呢?ProcessBuilder和Process兩個類有什么區別呢?
 原來,ProcessBuilder為進程提供了更多的控制,例如,可以設置當前工作目錄,還可以改變環境參數。而Process的功能相對來說簡單的多。
 ProcessBuilder是一個final類,有兩個帶參數的構造方法,你可以通過構造方法來直接創建ProcessBuilder的對象。而Process是一個抽象類,一般都通過Runtime.exec()和ProcessBuilder.start()來間接創建其實例。(有關Process類的詳細介紹可以看下一節。)
修改進程構造器的屬性將影響后續由該對象的 start() 方法啟動的進程,但從不會影響以前啟動的進程或 Java 自身的進程。
 ProcessBuilder類不是同步的。如果多個線程同時訪問一個 ProcessBuilder,而其中至少一個線程從結構上修改了其中一個屬性,它必須 保持外部同步。
很容易啟動一個使用默認工作目錄和環境的新進程:(沿用JDK7中的例子)
Process p = new ProcessBuilder("myCommand", "myArg").start();下面是一個利用修改過的工作目錄和環境啟動進程的例子:
ProcessBuilder pb = new ProcessBuilder("myCommand", "myArg1", "myArg2"); Map<String, String> env = pb.environment(); env.put("VAR1", "myValue"); env.remove("OTHERVAR"); env.put("VAR2", env.get("VAR1") + "suffix"); pb.directory("myDir"); Process p = pb.start();要利用一組明確的環境變量啟動進程,在添加環境變量之前,首先調用 Map.clear()。
Process類
Process類是一個抽象類(所有的方法均是抽象的),封裝了一個進程(即一個執行程序)。
Process 類提供了執行從進程輸入、執行輸出到進程、等待進程完成、檢查進程的退出狀態以及銷毀(殺掉)進程的方法。創建進程的方法可能無法針對某些本機平臺上的特定進程很好地工作,比如,本機窗口進程,守護進程,Microsoft Windows 上的 Win16/DOS 進程,或者 shell 腳本。創建的子進程沒有自己的終端或控制臺。它的所有標準 io(即 stdin、stdout 和 stderr)操作都將通過三個流 (getOutputStream()、getInputStream() 和 getErrorStream()) 重定向到父進程。父進程使用這些流來提供到子進程的輸入和獲得從子進程的輸出。因為有些本機平臺僅針對標準輸入和輸出流提供有限的緩沖區大小,如果讀寫子 進程的輸出流或輸入流迅速出現失敗,則可能導致子進程阻塞,甚至產生死鎖。 當沒有 Process 對象的更多引用時,不是刪掉子進程,而是繼續異步執行子進程。 對于帶有 Process 對象的 Java 進程,沒有必要異步或并發執行由 Process 對象表示的進程。
Process抽象類有以下6個抽象方法:
 destroy()
 殺掉子進程。
 exitValue()
 返回子進程的出口值。
 InputStream getErrorStream()
 獲得子進程的錯誤流。
 InputStream getInputStream()
 獲得子進程的輸入流。
 OutputStream getOutputStream()
 獲得子進程的輸出流。
 waitFor()
 導致當前線程等待,如果必要,一直要等到由該 Process 對象表示的進程已經終止。
如何創建Process對象?
 一般有兩種方法:
- 使用命令名和命令的參數選項構造ProcessBuilder對象,它的start方法執行命令,啟動一個進程,返回一個Process對象。
- Runtime.exec() 方法創建一個本機進程,并返回 Process 子類的一個實例。
Runtime.exec()
ProcessBuilder與Runtime.exec()的區別?
 ProcessBuilder.start() 和 Runtime.exec() 方法都被用來創建一個操作系統進程(執行命令行操作),并返回 Process 子類的一個實例,該實例可用來控制進程狀態并獲得相關信息。
 ProcessBuilder.start() 和 Runtime.exec()傳遞的參數有所不同,Runtime.exec()可接受一個單獨的字符串,這個字符串是通過空格來分隔可執行命令程序和參數的;也可以接受字符串數組參數。而ProcessBuilder的構造函數是一個字符串列表或者數組。列表中第一個參數是可執行命令程序,其他的是命令行執行是需要的參數。
通過查看JDK源碼可知,Runtime.exec最終是通過調用ProcessBuilder來真正執行操作的。
為了能夠詳細的說明ProcessBuilder和Runtime.exec的“功效”,下面先做一個測試jar包(ProcessJar.jar),這個jar包里就一個類,如下:
public class PrintArgs {public static void main(String args[]){System.out.println("This is a program test about Process, ProcessBuilder, Runtime.exec etc.");System.out.println("Now Print the args:");for(int i=0;i<args.length;i++){System.out.println(" [args-"+i+"]:"+args[i]);}} }然后放在classpath下。之后可以調用命令行:java -jar ProcessJar.jar [args1…n]
Runtime.getRuntime.exec的使用Demo:
package com.java;import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader;/*** Created by hidden on 2017/1/17.*/ public class RuntimeTest {public static void main(String[] args) {try {Process process = Runtime.getRuntime().exec("java -jar ProcessJar.jar args1 agrs2 args3");InputStream is = process.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(is));String line;while ((line = br.readLine()) != null) {System.out.println(line);}int exitCode = process.waitFor(); System.out.println(exitCode);} catch (IOException e) {e.printStackTrace();}} }輸出結果:
This is a program test about Process, ProcessBuilder, Runtime.exec etc. Now Print the args:[args-0]:args1[args-1]:agrs2[args-2]:args3 0由于調用Runtime.exec方法所創建的子進程沒有自己的終端或控制臺,因此該子進程的標準IO(如stdin,stdou,stderr)都通過Process.getOutputStream(),Process.getInputStream(),Process.getErrorStream()方法重定向給它的父進程了。用戶需要用這些stream來向子進程輸入數據或獲取子進程的輸出。
ProcessBuilder API
構造方法摘要
 ProcessBuilder(List<String> command)
 利用指定的操作系統程序和參數構造一個進程生成器。
 ProcessBuilder(String… command)
 利用指定的操作系統程序和參數構造一個進程生成器。
方法摘要
 command()
 返回此進程生成器的操作系統程序和參數。
 command(List<String> command)
 設置此進程生成器的操作系統程序和參數。
 command(String… command)
 設置此進程生成器的操作系統程序和參數。
 directory()
 返回此進程生成器的工作目錄。
 directory(File directory)
 設置此進程生成器的工作目錄。
 environment()
 返回此進程生成器環境的字符串映射視圖。 environment方法獲得運行進程的環境變量,得到一個Map,可以修改環境變量
 redirectErrorStream()
 通知進程生成器是否合并標準錯誤和標準輸出。
 redirectErrorStream(boolean redirectErrorStream)
 設置此進程生成器的 redirectErrorStream 屬性。
 start()
 使用此進程生成器的屬性啟動一個新進程。
ProcessBuilder Demo
這里演示一個ProcessBuilder的demo,和Runtime.exec()方法差不多,同樣是采用ProcessJar.jar進行測試。
public class ProcessBuilderTest {public static void main(String[] args) {List<String> params = new ArrayList<String>();params.add("java");params.add("-jar");params.add("ProcessJar.jar");params.add("args1");params.add("args2");params.add("args3");ProcessBuilder processBuilder = new ProcessBuilder(params); // System.out.println(processBuilder.directory()); // System.out.println(processBuilder.environment());processBuilder.redirectErrorStream(true);try {Process process = processBuilder.start();BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));String line;while ((line = br.readLine()) != null) {System.out.println(line);}int exitCode = process.waitFor();System.out.println("exitCode = "+exitCode);} catch (IOException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}} }輸出結果:
This is a program test about Process, ProcessBuilder, Runtime.exec etc. Now Print the args:[args-0]:args1[args-1]:args2[args-2]:args3 exitCode = 0為了更形象的說明ProcessBuilder的用法,下面再舉幾個例子:
/*** 查看"D:\"目錄, Windows系統下查看目錄的命令是dir*/ public static void checkDirectory() throws IOException {ProcessBuilder processBuilder = new ProcessBuilder("cmd","/c","dir");processBuilder.directory(new File("D:/"));Process process = processBuilder.start();BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK"));String line;while ((line = br.readLine()) != null) {System.out.println(line);} }/*** 查看ip地址【Windows系統下】*/ public static void checkPhysicAddress() {ProcessBuilder processBuilder = new ProcessBuilder("ipconfig", "/all");try {Process process = processBuilder.start();BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK"));String line;while ((line = br.readLine()) != null) {if (line.indexOf("IPv4") != -1) {System.out.println(line);}}} catch (IOException e) {e.printStackTrace();} }歡迎跳轉到本文的原文鏈接:https://honeypps.com/java/process-builder-quick-start/
歡迎支持筆者新作:《深入理解Kafka:核心設計與實踐原理》和《RabbitMQ實戰指南》,同時歡迎關注筆者的微信公眾號:朱小廝的博客。
 
總結
以上是生活随笔為你收集整理的浅析ProcessBuilder的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: RabbitMQ Network Par
- 下一篇: RocketMQ配置
