我的编程空间,编程开发者的网络收藏夹
学习永远不晚

解决Process.getInputStream()阻塞的问题

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

解决Process.getInputStream()阻塞的问题

Process.getInputStream()阻塞问题

Java中


Runtime.getInstance().exec (String cmd)

或者


new ProcessBuilder(String cmd).start()

都可以产生子进程对象Process。通过调用Process对象的waitFor()方法可以使主进程进入等待状态,直至子进程执行完毕,再进行下一步工作。如果对子进程处理不当,有可能造成主进程阻塞,整个程序死掉。

java Api中关于Process说的是:

ProcessBuilder.start() 和 Runtime.exec 方法创建一个本机进程,并返回 Process 子类的一个实例,该实例可用来控制进程并获取相关信息。Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。

创建进程的方法可能无法针对某些本机平台上的特定进程很好地工作,比如,本机窗口进程,守护进程,Microsoft Windows 上的 Win16/DOS 进程,或者 shell 脚本。创建的子进程没有自己的终端或控制台。它的所有标准 io(即 stdin,stdout,stderr)操作都将通过三个流 (getOutputStream(),getInputStream(),getErrorStream()) 重定向到父进程。父进程使用这些流来提供到子进程的输入和获得从子进程的输出。因为有些本机平台仅针对标准输入和输出流提供有限的缓冲区大小,如果读写子进程的输出流或输入流迅速出现失败,则可能导致子进程阻塞,甚至产生死锁。

在对getOutputStream(),getInputStream(),getErrorStream()的描述中,有个注意事项:对其输出流和错误流进行缓冲是一个好主意!嗯,好抽象啊!

问题正在于此,Process.getInputStream()和Process.getErrorStream()分别返回Process的标准输出流和错误流,两个流如果处理不当,其缓冲区不能被及时清除而被塞满,则进程被阻塞,即使调用Process.destory()也未必能销毁被阻塞的子进程。

如果尝试同步获取Process的输出流和错误流进行处理,未必有效,顺序执行过程中,输出流和错误流常常不能得到及时处理。解决方案有两个。

方案一:并发获取Process的输出流和错误流

通过启动两个线程来并发地读取和处理输出流和错误流,懒得打开IDE了,就大概敲一下代码吧,可能有错误,如下:

调用者:


class ProcessExecutor
{
 private Process p;
 private List<String> outputList;
 private List<String> errorOutputList;
 public ProcessExecutor(Process p) throws IOException
 {
  if(null == p)
  {
   throw new IOException("the provided Process is null");
  }
  this. p = p;
 }
 public List<String> getOutputList()
 {
  return this. outputList;
 }
 public List<String> getErrorOutputList()
 {
  return this.errorOutputList;
 }
 public int execute()
 {
  int rs = 0;
  Thread outputThread = new ProcessOutputThread(this.p.getInputStream());
  Thread errorOutputThread = new ProcessOutputThread(this.p.getErrorStream());
  outputThread.start();
  errorOutputThread.start();
  rs = p.waitFor();
  outputThread.join();
  errorOutputThread.join();
  this.outputList = outputThread.getOutputList();
  this.errorOutputList = errorOutputThread.getOutputList();
  return rs;
 }
}

流处理线程


class ProcessOutputThread extends Thread
{
 private InputStream is;
 private List<String> outputList;
 public ProcessOutputThread(InputStream is) throws IOException
 {
  if(null == is)
  {
   throw new IOException("the provided InputStream is null");
  }
  this. is = is;
  this.outputList = new ArrayList<String>();
 }
 public List<String> getOutputList()
 {
  return this. outputList;
 }
 @Override
 public void run()
 {
  InputStreamReader ir = null;
  BufferedReader br = null;
  try
  {
   ir = new InputStreamReader(this.is);
   br = new BufferedReader(ir);
   String output = null;
   while(null != (output = br.readLine()))
   {
    print(output);
    this.outputList.add(output);
   }
  }
  catch(IOException e)
  {
   e.print();
  }
  finally
  (
   try
   {
    if(null != br)
    {
     br.close();
    }
    if(null != ir)
    {
     ir.close();
    }
    if(null != this.is)
    {
     this.is.close();
    }
   }
   catch(IOException e)
   {
    e.print();
   }
  )
 }
}

方案二:用ProcessBuilder的redirectErrorStream()方法合并输出流和错误流


public int execute()
{
 int rs = 0;
 String[] cmds = {...};//command and arg  
 ProcessBuilder builder = new ProcessBuilder(cmds);  
 builder.redirectErrorStream(true);  
 Process process = builder.start();  
 BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));  
 String output = null;  
 while (null != (readLine = br.readLine()))
 {  
     print(output);   
 }  
 rs = process.waitFor();
 return rs;
} 

Java Process 阻塞测试总结

Process阻塞原因:输入流和错误流分开的,没有处理,就会发生阻塞,归根结底本质上是bio引起的io阻塞问题。

getInputStream,getErrorSteam就是获取脚本或者命令的控制台回显信息,前者获取的是标准输出的回显信息,后者获取的是标准错误的回显信息

Process原理:使用Runtime.getRuntime().exec(cmd)会在当前进程建立一个子进程,子进程由于没有控制台,它的标准输出和标准错误就会返回给父进程Process,因此通过getInputStream和getErrorStream就可以获取到这些信息。

测试代码如下:


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class JavaExeBat {
        public JavaExeBat() {
        }
        public static void main(String[] args) {
                Process p;
                //test.bat中的命令是ipconfig/all
                String cmd="sh test.sh ";
                //String cmd="ping 127.0.0.1 -c 4";
 
                try {
                        //执行命令
                        p = Runtime.getRuntime().exec(cmd);
                        //取得命令结果的输出流
                        //输出流
                        InputStream fis=p.getInputStream();
                        //错误流
                        InputStream ferrs=p.getErrorStream();
                        //用一个读输出流类去读
                        InputStreamReader isr=new InputStreamReader(fis);
                        InputStreamReader errsr=new InputStreamReader(ferrs);
                        //用缓冲器读行
                        BufferedReader br=new BufferedReader(isr);
                        BufferedReader errbr=new BufferedReader(errsr);
                        String line=null;
                        String lineerr = null;
                        //直到读完为止
                        while((line=br.readLine())!=null) {
                        //有可能发生阻塞的问题
                                System.out.println("return input Str:" + line);
                        }
                        while((lineerr=errbr.readLine())!=null){
                        //有可能发生阻塞的问题
                                System.out.println("return err Str:" + lineerr);
                        }
                        int exitVal = p.waitFor();
                        System.out.println("exitVal:" + exitVal);
                } catch (Exception e) {
                        e.printStackTrace();
                }
        }
}

test.sh如下


#!/bin/bash
 
for((i=0; i < 100000; i++));do
         //输出的标准输出
        echo "testaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        //输出到标准错误
        echo "testaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 1>&2
done

经过测试发现,如果JavaExeBat.java文件中只开启标准输出或者标准错误时,进程就会夯住,无法通过waiteFor获取其返回值,因为脚本中分别输出了100000w条信息到标准输出和标准错误,而下述代码只处理了getInputStream,导致标准错误输出流的信息太多返回给当前进程,没有得到处理,因此阻塞。

代码如下:


p = Runtime.getRuntime().exec(cmd);
                        //取得命令结果的输出流
                        //输出流
                        InputStream fis=p.getInputStream();
                        //用一个读输出流类去读
                        InputStreamReader isr=new InputStreamReader(fis);
                        //用缓冲器读行
                        BufferedReader br=new BufferedReader(isr);
                        String line=null;
                        //直到读完为止
                        while((line=br.readLine())!=null) {
                        //有可能发生阻塞的问题
                                System.out.println("return input Str:" + line);
                        }
                        int exitVal = p.waitFor();
                        System.out.println("exitVal:" + exitVal);

把上述代码中的getInputStream换做getErrorStream,也会夯住进程,因为同样只处理了两者中一者,即标准错误。

那么能不能同步处理两个流信息呢?代码如下:


try {
                        //执行命令
                        p = Runtime.getRuntime().exec(cmd);
                        //取得命令结果的输出流
                        //输出流
                        InputStream fis=p.getInputStream();
                        //错误流
                        InputStream ferrs=p.getErrorStream();
                        //用一个读输出流类去读
                        InputStreamReader isr=new InputStreamReader(fis);
                        InputStreamReader errsr=new InputStreamReader(ferrs);
                        //用缓冲器读行
                        BufferedReader br=new BufferedReader(isr);
                        BufferedReader errbr=new BufferedReader(errsr);
                        String line=null;
                        String lineerr = null;
                        //直到读完为止
                        while((line=br.readLine())!=null) {
                        //有可能发生阻塞的问题
                                System.out.println("return input Str:" + line);
                        }
                        while((lineerr=errbr.readLine())!=null){
                        //有可能发生阻塞的问题
                                System.out.println("return err Str:" + lineerr);
                        }
                        int exitVal = p.waitFor();
                        System.out.println("exitVal:" + exitVal);
                } catch (Exception e) {
                        e.printStackTrace();
                }
        }

测试过后发现也不行,因为是同步的,就会有先后顺序,也会发生阻塞,测试方法,将test.sh改为只打印标准错误,就会发现标准错误处理被阻塞,脚本如下:


#!/bin/bash
 
for((i=0; i < 100000; i++));do
        //输出到标准错误
        echo "testaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 1>&2
done

解决办法思路:

(1)并发处理两个流信息,开启两个线程分别处理输出流与错误流

(2)将两个流合并为一个流解决示例:

第一种思路:


class ProcessExecutor  
    {  
        private Process p;  
        private List<String> outputList;  
        private List<String> errorOutputList;  
        public ProcessExecutor(Process p) throws IOException  
        {  
            if(null == p)  
            {  
                throw new IOException("the provided Process is null");  
            }  
            this. p = p;  
        }  
        public List<String> getOutputList()  
        {  
            return this. outputList;  
        }  
        public List<String> getErrorOutputList()  
        {  
            return this.errorOutputList;  
        }  
        public int execute()  
        {  
            int rs = 0;  
            Thread outputThread = new ProcessOutputThread(this.p.getInputStream());  
            Thread errorOutputThread = new ProcessOutputThread(this.p.getErrorStream());  
            outputThread.start();  
            errorOutputThread.start();  
            rs = p.waitFor();  
            outputThread.join();  
            errorOutputThread.join();  
            this.outputList = outputThread.getOutputList();  
            this.errorOutputList = errorOutputThread.getOutputList();  
            return rs;  
        }  
    }  
    
    class ProcessOutputThread extends Thread  
    {  
        private InputStream is;  
        private List<String> outputList;  
        public ProcessOutputThread(InputStream is) throws IOException  
        {  
            if(null == is)  
            {  
                throw new IOException("the provided InputStream is null");  
            }  
            this. is = is;  
            this.outputList = new ArrayList<String>();  
        }  
        public List<String> getOutputList()  
        {  
            return this. outputList;  
        }  
        @Override  
        public void run()  
        {  
            InputStreamReader ir = null;  
            BufferedReader br = null;  
            try  
            {  
                ir = new InputStreamReader(this.is);  
                br = new BufferedReader(ir);  
                String output = null;  
                while(null != (output = br.readLine()))  
                {  
                    print(output);  
                    this.outputList.add(output);  
                }  
            }  
            catch(IOException e)  
            {  
                e.print();  
            }  
            finally  
            (  
                try  
                {  
                    if(null != br)  
                    {  
                        br.close();  
                    }  
                    if(null != ir)  
                    {  
                        ir.close();  
                    }  
                    if(null != this.is)  
                    {  
                        this.is.close();  
                    }  
                }  
                catch(IOException e)  
                {  
                    e.print();  
                }  
            )  
        }  
    }  

第二种思路:使用ProcessBuilder,将其redirectErrorStream(true);将输出流与错误流合并


 public int execute()  
    {  
        int rs = 0;  
        String[] cmds = {...};//command and arg    
        ProcessBuilder builder = new ProcessBuilder(cmds);    
        builder.redirectErrorStream(true);    
        Process process = builder.start();    
        BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));    
        String output = null;    
        while (null != (readLine = br.readLine()))  
        {    
            print(output);     
        }    
        rs = process.waitFor();  
        return rs;  
    }  

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

解决Process.getInputStream()阻塞的问题

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何解决Process.getInputStream()阻塞的问题

这篇文章主要介绍了如何解决Process.getInputStream()阻塞的问题,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Process.getInputStream
2023-06-15

怎么解决TCP socket的阻塞问题

小编给大家分享一下怎么解决TCP socket的阻塞问题,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!解决TCP socket的阻塞问题大家知道,tcp的读和写是
2023-06-22

quartz任务阻塞问题怎么解决

解决Quartz任务阻塞问题的方法有以下几种:1. 增加线程池大小:默认情况下,Quartz使用一个线程执行任务。如果任务较多或者任务执行时间较长,可以通过配置增加线程池的大小,以增加任务并发量,避免阻塞。可以通过配置文件或者编程方式设置线
2023-09-18

Android中bindservice阻塞问题怎么解决

在Android中,绑定服务(bindService)是一种异步操作,它不会阻塞主线程。但有时候可能会出现与服务通信的操作阻塞了UI线程,导致应用无响应。以下是几种解决bindService阻塞问题的方法:使用Handler:在主线程中使
Android中bindservice阻塞问题怎么解决
2024-02-29

如何解决基于Process#waitFor()阻塞问题

这篇文章主要介绍了如何解决基于Process#waitFor()阻塞问题,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Process#waitFor()阻塞问题有时需要在程序
2023-06-22

tomcat高并发阻塞问题怎么解决

要解决Tomcat高并发阻塞问题,可以考虑以下几个方面:1. 调整Tomcat配置:增加Tomcat的线程池大小、调整连接超时时间等,以提高Tomcat的并发处理能力。2. 使用NIO或异步Servlet:Tomcat默认使用的是BIO方式
2023-10-12

Redis bigkeys命令会阻塞问题如何解决

这篇文章主要介绍“Redis bigkeys命令会阻塞问题如何解决”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Redis bigkeys命令会阻塞问题如何解决”文章能帮助大家解决问题。一、 顺丰高
2023-07-05

如何解决java调用process线程阻塞的问题

这篇文章将为大家详细讲解有关 如何解决java调用process线程阻塞的问题,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。java调用process线程阻塞问题项目需求中涉及java调用.bat文件进行
2023-06-15

Java中socket使用getInputStream()阻塞问题怎么解决

这篇文章主要介绍“Java中socket使用getInputStream()阻塞问题怎么解决”,在日常操作中,相信很多人在Java中socket使用getInputStream()阻塞问题怎么解决问题上存在疑惑,小编查阅了各式资料,整理出简
2023-06-22

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录