本篇仍旧是源于最近的工作,总结一下纪念那些年埋下的坑...

背景故事

  • 需求:“使用进程方式启动另一个程序!”

  • 开发:“OK! Runtime.getRuntime().exec("xxxx")”

  • 需求:“启动以后能看到输出消息不!”

  • 开发:“OK!”

  1. Process process = null;
  2. try {
  3. process = Runtime.getRuntime().exec("ipconfig /all");
  4. } catch (IOException e) {
  5. e.printStackTrace();
  6. }
  7. try {
  8. String line;
  9. InputStream is = process.getInputStream();
  10. BufferedReader br = new BufferedReader(new InputStreamReader(is,"GBK"));
  11. while(null != (line = br.readLine())){
  12. System.out.println(line);
  13. }
  14. System.out.println("---------------------------------------------------------------------------------------");
  15. InputStream is_error = process.getErrorStream();
  16. BufferedReader br_error = new BufferedReader(new InputStreamReader(is_error,"GBK"));
  17. while(null != (line = br_error.readLine())){
  18. System.out.println(line);
  19. }
  20. } catch (IOException e) {
  21. e.printStackTrace();
  22. }

于是,神坑挖好了!

遇到的问题

由于运行的程序比较复杂,有可能出现错误输出。这时就不好保证是错误输出还是标准输出哪个先到。但是上面的程序中,使用了同步的方式输出子进程的消息,结果就导致了子进程阻塞。

解决方案1:使用缓冲区缓存消息

这个可以参考CSDN的帖子

解决方案2:使用ProcessBuilder合并标准输出和错误

仍然源自于上面的博客:

  1. try{
  2. String[] cmds = {"ipconfig","/all"};
  3. ProcessBuilder builder = new ProcessBuilder(cmds);
  4. //合并输出流和错误流
  5. builder.redirectErrorStream(true);
  6. //启动进程
  7. Process process = builder.start();
  8. //获得输出流
  9. BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream(),"GBK"));
  10. String line = null;
  11. while (null != (line = br.readLine())){
  12. System.out.println(line);
  13. }
  14. }catch(IOException e){
  15. e.printStackTrace();
  16. }

上面的代码中builder.redirectErrorStream(true);就可以帮助你把错误合并到标准输出里面。

于是,很好奇这个ProcessBuilder到底什么东东。

阅读API —— 什么是ProcessBuilder

ProcessBuilder用于创建操作系统进程,每个ProcessBuilder实例都管理一个进程属性集合。通过调用start()方法,可以通过这些属性创建出一个进程。start()方法可以被多次调用,来创建多个独立的进程。

每个builder管理着下面的进程属性:

cmmand

命令,比如{“ipcofig”,"/all"}

environment

环境变量,子进程会直接使用当前进程的环境变量。环境变量是独立的,因此可以被修改,但是不会影响其他的进程。

directory

工作目录,如果返回的是Null,说明当前目录使用的是系统变量user.dir所在的目录。

redirectErrorStream属性

默认是false。Flase意味着标准输出和标准错误是两个独立的流,可以通过Process.getInputStream()和Process.getErrorStream()方法获得。

如果这个值设置为true,那么标准错误将会合并到标准输出中,并且发往同一个目标地址(这种特性使得错误消息可以很方便的和输出消息一起管理),此时,如果你再想要单独获取错误输出流,就会得到null。

线程安全

注意这个类不是线程安全的,因此如果多个线程使用ProcessBuilder实例,并且修改属性,那么可能会造成冲突。因此需要在外面进行线程同步。

启动

可以简单的向下面这样启动一个进程:

  1. Process p = new ProcessBuilder("myCommand", "myArg").start();

样例

下面是官方文档中给出的样例,样例中修改了工作目录以及环境变量,并且把标准错误和标准输出合并输出到日志文件中:

  1. ProcessBuilder pb = new ProcessBuilder("myCommand", "myArg1", "myArg2");
  2. Map<String, String> env = pb.environment();
  3. env.put("VAR1", "myValue");
  4. env.remove("OTHERVAR");
  5. env.put("VAR2", env.get("VAR1") + "suffix");
  6. pb.directory(new File("myDir"));
  7. File log = new File("log");
  8. pb.redirectErrorStream(true);
  9. pb.redirectOutput(Redirect.appendTo(log));
  10. Process p = pb.start();
  11. assert pb.redirectInput() == Redirect.PIPE;
  12. assert pb.redirectOutput().file() == log;
  13. assert p.getInputStream().read() == -1;

Java程序员的日常 —— 多进程开发IO阻塞问题的更多相关文章

  1. Java程序员的日常 —— 多进程开发

    最近再弄进程管理相关的工作,因此必要的就涉及到各种系统下关于进程的管理. 这里简单的介绍下: 如何在Java中执行命令 在windows下肯定是dos命令了,而在linux则为shell命令.执行的方 ...

  2. Java程序员的日常——经验贴(纯干货)

    工作当中遇到的事情比较杂,因此涉及的知识点也很多.这里暂且记录一下,今天遇到的知识点,纯干货~ 关于文件的解压和压缩 如果你的系统不支持tar -z命令 如果是古老的Unix系统,可能并不认识tar ...

  3. 2018年,Java程序员转型大数据开发,是不是一个好选择?

    近日网上有一篇关于Java程序员职场生存现状的文章“2017年 Java 程序员,风光背后的危机”,在Java程序员圈子里引起了广泛关注和热议. 2017年,Java 程序员面临更加激烈的竞争. 不得 ...

  4. Java程序员的日常—— Properties文件的读写

    在日常的Java程序开发中,Properties文件的读写是很常用的.经常有开发系统通过properties文件来当做配置文件,方便用户对系统参数进行调整. 那么本片就来简单的介绍下,如何使用Prop ...

  5. Java程序员的日常—— 《编程思想》关于类的使用常识

    Java虽然利用JVM,让程序员可以放心大胆的使用,可是仍然会出现内存泄露等问题.世上没有绝对的银弹,因此也不能完全把所有的任务都交给JVM,了解Java中的初始化与垃圾回收还是必不可少的知识. 关于 ...

  6. Java程序员的日常—— 垃圾回收中引用类型的作用

    在Java里面,是不需要太过于关乎垃圾回收,但是这并不意味着开发者可以不了解垃圾回收的机制,况且在java中内存泄露也是家常便饭的事情.因此了解垃圾回收的相关知识就显得很重要了. 引用,在垃圾回收中是 ...

  7. Java程序员的日常—— Arrays工具类的使用

    这个类在日常的开发中,还是非常常用的.今天就总结一下Arrays工具类的常用方法.最常用的就是asList,sort,toStream,equals,copyOf了.另外可以深入学习下Arrays的排 ...

  8. Java程序员的日常——经验贴(纯干货)二

    继昨天的经验贴,今天的工作又收获不少. windows下编辑器会给文件添加BOM 在windows的编辑器中,为了区分编码,通常会添加一个BOM标记.比如,记事本.nodepade++.sublime ...

  9. Java程序员的日常——SpringMVC+Mybatis开发流程、推荐系统

    今天大部分时间都在写业务代码,然后算是从无到有的配置了下spring与mybatis的集成. SpringMVC+Mybatis Web开发流程 配置数据源 在applicationContext.x ...

随机推荐

  1. 18.safari 安装后flash还是提示安装 flash,视频不能播放

    第一步: safari---->偏好设置(首先安装最新Mac adobe flash) 第二步: 第三步:点击进入 第四步:再次打开safar,大功告成!

  2. ubuntu下python 2.7与python 3.X的转换

    ubuntu下python 2.7与python 3.X的转换 由于ubuntu本身自带python 2.7,而python 3.X与2.7有很多不同,所以在使用python 3.X时会带来诸多不便. ...

  3. java 验证码

    package lizikj.bigwheel.pcsystem.util;import javax.imageio.ImageIO; import java.awt.*; import java.a ...

  4. iOS进阶_地图定位

    一.定位步骤 1.Xcode自带地图,直接先引入头文件 #import <CoreLocation/CoreLocation.h> 2.CLLocation框架中的CLLocationMa ...

  5. struts2 validation.xml 注意点

    1.首先应该注意validation.xml的名字,一定要以Action的类名加“-validation.xml”作为文件名.入LoginAction-validation.xml. 2.LoginA ...

  6. Android中<original-package>标签含义

    在AndroidManifest.xml中,<original-package>与<manifest package=...>中的区别:<original-package ...

  7. day10---multiprocess 多进程

    multiprocess Queue  \ Pipe 只是实现进程间数据的传递 Manager 实现了进程间数据的共享,即多个进程可以修改同一份数据   进程模块 multiprocessing #! ...

  8. debian8-server install record

    1. install necessary softwares apt-get install vim git ssh 2. install input method apt-get install f ...

  9. ios图片添加文字或者水印

    在项目中,我们会对图片做一些处理,但是我们要记住,一般在客户端做图片处理的数量不宜太多,因为受设备性能的限制,如果批量的处理图片,将会带来交互体验性上的一些问题.首先让我们来看看在图片上添加文字的方法 ...

  10. ubuntu 14.04 下svn + apache2 配置

    1.svn的配置 sudo apt-get install subversion // 安装svn mkdir /home/svn // 创建仓库 svnadmin create /home/svn/ ...