Process简介
我们在实际Java开发工作中可能会遇到调用操作系统命令的场景,比如查看下文件夹,执行下sh/exe文件等等,那么我们就要用到Process了!
首先,打开API来认识下Process :
java.lang 类 Process java.lang.Object 继承者 java.lang.Process public abstract class Process extends Object ProcessBuilder.start() 和 Runtime.exec 方法创建一个本机进程,并返回 Process 子类的一个实例,该实例可用来控制进程并获得相关信息。Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。
我们看到,Process是个抽象类,继承自“全民祖先”Object,有两种方式可以创建Process子类的实例,以及一系列进程交互方法:
方法摘要 abstract void destroy() 杀掉子进程。 abstract int exitValue() 返回子进程的出口值。 abstract InputStream getErrorStream() 获取子进程的错误流。 abstract InputStream getInputStream() 获取子进程的输入流。 abstract OutputStream getOutputStream() 获取子进程的输出流。 abstract int waitFor() 导致当前线程等待,如有必要,一直要等到由该 Process 对象表示的进程已经终止。
Process实战操练
学了那么多理论,还不如来个demo,我们来一个用“ping 百度网址”的例子吧,请看:
import java.io.IOException; public class ProcessDemo { public static void main(String[] args) { try { Process process = Runtime.getRuntime().exec("ping www.baidu.com"); System.out.println("任务执行完毕!"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
顺利运行,调用成功。但是结果是调用ping被挂到后台运行,程序直接打印了“任务执行完毕!”,而我们想要的效果是先把ping操作执行完成后,再输出“任务执行完毕!”。
好吧,Process提供了WaitFor和getInputStream两个方法,这两个方法都是阻塞java线程,等待脚本返回或结束后,再继续执行java程序,OK,那说改就改!
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class ProcessDemo { public static void main(String[] args) { try { Process process = Runtime.getRuntime().exec("ping www.baidu.com"); BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(process.getInputStream(),"gbk")); String line; while ((line = bufferedReader.readLine()) != null) { System.out.println(line); } System.out.println("任务执行完毕!"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
import java.io.IOException; public class ProcessDemo { public static void main(String[] args) { try { Process process = Runtime.getRuntime().exec("ping www.baidu.com"); process.waitFor(); System.out.println("任务执行完毕!"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
这就是我们想要的啦~
问题进阶
仔细的同学可能发现了,API文档里还有这么一句话:“创建进程的方法可能无法针对某些本机平台 上的特定进程很好地工作,比如,本机窗口进程,守护进程,Microsoft Windows 上的 Win16/DOS 进程,或者 shell 脚本。创建的子进程没有自己的终端或控制台。它的所有标准 io(即 stdin、 stdout 和 stderr)操作都将通过三个流 (getOutputStream()
、getInputStream()
和 getErrorStream()
) 重定向到父进程。父进程使用这些流来提供到子进程 的输入和获得从子进程的输出。因为有些本机平台仅针对标准输入和输出流提供有限的缓冲区大小,如果读写子进程的输出流或输入流迅速出现失败,则可能导致子进程阻塞,甚至产生死锁。”
这句话晦涩难懂,但可以简单地归纳一下,针对可执行程序的输入输出每个平台都会提供缓冲区,当没有及时把缓冲数据读出,且可执行程序在短时间内有大量数据输入缓冲区的话,缓冲区撑满,进程就会被阻塞。
好吧,那意思很清楚了,就是让我们在写Process程序的时候,尽量主动把可执行程序的输入和输出读出来,不要让它们长时间留在缓冲区。
上代码,我们先创建一个线程类,它主要负责不停地来读出Process所调用脚本的输出数据(主要的是读出error信息):
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; public class CleanInputCache extends Thread { private InputStream is; private String type; public CleanInputCache(InputStream is, String type) { this.is = is; this.type = type; } public void run() { try { InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line = null; while ((line = br.readLine()) != null) System.out.println(type + ">>>" + line); } catch (IOException ioe) { ioe.printStackTrace(); } } }
然后改造一下以上的“ping百度网址”的程序:
import java.io.IOException; public class ProcessDemo { public static void main(String[] args) { try { Process process = Runtime.getRuntime().exec("ping www.baidu.com"); new CleanInputCache(process.getInputStream(),"INFO").start(); new CleanInputCache(process.getErrorStream(),"ERROR").start(); process.waitFor(); System.out.println("任务执行完毕!"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
大功告成,这样程序再也不用担心阻塞了,放心奔跑吧,process!!!