前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java程序员的日常 —— 多进程开发IO阻塞问题

Java程序员的日常 —— 多进程开发IO阻塞问题

作者头像
用户1154259
发布2018-01-17 14:59:59
1.3K0
发布2018-01-17 14:59:59
举报

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

背景故事

  • 需求:“使用进程方式启动另一个程序!”
  • 开发:“OK! Runtime.getRuntime().exec("xxxx")”
  • 需求:“启动以后能看到输出消息不!”
  • 开发:“OK!”
代码语言:javascript
复制
Process process = null;
try {
    process = Runtime.getRuntime().exec("ipconfig /all");
} catch (IOException e) {
    e.printStackTrace();
}
try {
    String line;
    InputStream is = process.getInputStream();
    BufferedReader br = new BufferedReader(new InputStreamReader(is,"GBK"));
        
    while(null != (line = br.readLine())){
        System.out.println(line);
    }
    System.out.println("---------------------------------------------------------------------------------------");
    InputStream is_error = process.getErrorStream();
    BufferedReader br_error = new BufferedReader(new InputStreamReader(is_error,"GBK"));
        
    while(null != (line = br_error.readLine())){
        System.out.println(line);
    }
} catch (IOException e) {
    e.printStackTrace();
}

于是,神坑挖好了!

遇到的问题

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

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

这个可以参考CSDN的帖子

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

仍然源自于上面的博客:

代码语言:javascript
复制
try{
    String[] cmds = {"ipconfig","/all"};
    ProcessBuilder builder = new ProcessBuilder(cmds);    
    
    //合并输出流和错误流
    builder.redirectErrorStream(true);    
             
    //启动进程
    Process process = builder.start();    
             
    //获得输出流
    BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream(),"GBK"));    
    String line = null;    
    while (null != (line = br.readLine())){    
        System.out.println(line);
    }    
}catch(IOException e){
    e.printStackTrace();
}

上面的代码中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实例,并且修改属性,那么可能会造成冲突。因此需要在外面进行线程同步。

启动

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

代码语言:javascript
复制
 Process p = new ProcessBuilder("myCommand", "myArg").start();
样例

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

代码语言:javascript
复制
 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(new File("myDir"));
 File log = new File("log");
 pb.redirectErrorStream(true);
 pb.redirectOutput(Redirect.appendTo(log));
 Process p = pb.start();
 assert pb.redirectInput() == Redirect.PIPE;
 assert pb.redirectOutput().file() == log;
 assert p.getInputStream().read() == -1;
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-06-04 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景故事
  • 遇到的问题
  • 解决方案1:使用缓冲区缓存消息
  • 解决方案2:使用ProcessBuilder合并标准输出和错误
  • 阅读API —— 什么是ProcessBuilder
    • cmmand
      • environment
        • directory
          • redirectErrorStream属性
            • 线程安全
              • 启动
                • 样例
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档