zoukankan      html  css  js  c++  java
  • 记录一次由于waitfor()导致死锁的bug

    在上周的开发中由于使用了proccess.waitfor()语句导致了进程死锁

    问题描述:

    在我开发的数据采集的模块,需要调用服务器上的一个python脚本来进行sql语法的转换所以我使用了java自带的方法

    Process proc;
    try {

    proc = Runtime.getRuntime().exec(python filePath);
        LOGGER.info("py-mysql2pgsql is successful.....");


    proc.waitFor();
    LOGGER.info("waitFor ... ... ...");

    return true;
    } catch (Exception e) {
    e.printStackTrace();
    return false;
    }

     

    当时自测在我们的开发环境使用这段代码并没有什么问题,但是当项目发布到测试环境时出现了进程死锁的问题

    由于进程被锁住导致任务被挂起,不报错也不继续往下执行。

    这个问题让我花费了很长时间才锁定了bug,由于当时在开发环境并没有什么问题,所以也就没有想到是waifor

    导致了死锁,所以在代码中记录了很多的日志才找到。

    接下来看看具体的日志信息:

     

     可以看到项目的日志打印到

    py-mysql2pgsql is successful..... 就停止不动了。

    原因:

     

    在网上找了很多产生这种问题的原因,但是以下这种是我比较认同的:
    在调用Runtime.getRuntime().exec() 方法时会启动一个子进程来专门执行指定的linux命令,在子进程执行
    linux命令的同时,我们使用了 proccess.waitfor()命令使主进程挂起等待子进程执行完毕。但是子进程在执行
    时会产生一些日志信息主进程可以使用 process.getInputStream()和 process.getErrorStream() 来获取子进
    程的打印信息和日志信息。子进程在执行的期间会不断向主进程发送执行信息,但是主进程已经使用waitFor挂
    起,导致主进程与子进程之间的缓冲区填满导致子进程也被挂起,引起了死锁的问题。
    至于我当时在开发环境为什么没有遇到这个问题,现在还没有找到原因,目前判断可能是开发环境的硬件资源和
    内存缓冲区优于测试环境吧。后续会跟进这个问题。

    解决方案:

        指导了问题所在,接下了就是解决问题。既然是因为主进程与子进程之间的缓冲区堵塞导致的死锁,那么我们可以创建新的线程来消费缓冲区的数据:

        具体代码如下:

        

    
    
    Process proc;
    try {
    proc = Runtime.getRuntime().exec(python filePath);
    LOGGER.info("py-mysql2pgsql is successful.....");
    //在 exec方法和waitFor()方法之间加上消费缓冲区数据的方法
    //之前说过子进程给主进程反回的数据分为 日志信息数据 process.getInputStream() 和 错误信息数据 process.getErrorStream()

    consumeInputStream(proc.getInputStream());
    consumeInputStream(proc.getErrorStream());
    proc.waitFor();
    LOGGER.info("waitFor ... ... ...");

    return true;
    } catch (Exception e) {
    e.printStackTrace();
    return false;
    }
    }

      

    public static String consumeInputStream(InputStream stream){
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(stream));
            String s ;
            StringBuilder sb = new StringBuilder();
            while((s=br.readLine())!=null){
                System.out.println(s);
                sb.append(s);
            }
            return sb.toString();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
    

      当然,你也可以重新开两个线程来消费 inputStream 和 errorStream.

        

     

     

  • 相关阅读:
    BAT 大厂最流行的测试技术体系与测试职业发展晋级指南
    通知 | 2021 第一期《测试开发实战宝典》赠书活动顺利结束!
    你还缺个好工作?但7月毕业季即将来临怎么办
    一图看懂云栖大会「云原生」重磅发布
    阿里云重磅发布业务中台产品 BizWorks,中台发展进入下一个阶段
    云栖·追踪云原生|Serverless入围阿里云十大核心技术
    先行一步,7大技术创新和突破,阿里云把 Serverless 领域的这些难题都给解了
    New homework
    German
    suggestion
  • 原文地址:https://www.cnblogs.com/yooc1994/p/10746379.html
Copyright © 2011-2022 走看看