java使用Process调用exe程序及Process.waitFor()死锁问题解决

 更新时间:2022年12月20日 09:05:42   作者:小肥羊冲冲冲  
在编写Java程序时,有时候我们需要调用其他的诸如exe,shell这样的程序或脚本,下面这篇文章主要给大家介绍了关于java使用Process调用exe程序及Process.waitFor()死锁问题解决的相关资料,需要的朋友可以参考下

前言

最近在开发android的同时也在开发java ,碰到了需要使用java 程序调用exe的需求,这里我使用的 process 来调用的。该篇文章 读完需要8+分钟,文章类型为 小白入门类型,此处主要记录,方便以后学习补充… 如有不正确的地方还望海涵 及 指出….

文章参考

process参考

waitfor挂起解析

1. 使用process调用exe程序

ProcessBuilder pb = new ProcessBuilder("C:\\Debug\\TestRedis.exe", keyNmae);
pb.redirectErrorStream(true);
Process process = pb.start();
//可能导致进程阻塞,甚至死锁
int ret = process.waitFor();
System.out.println("return value:"+ret);
System.out.println(process.exitValue());
byte[] bytes = new byte[process.getInputStream().available()];
process.getInputStream().read(bytes);
System.out.println(new String(bytes));
// ProcessBuilder api 方法
public ProcessBuilder(String... command) {
        this.command = new ArrayList<>(command.length);
        for (String arg : command)
            this.command.add(arg);
    }

首先我们先使用 processBuilder 创建出该对象,该对象我这里暂时使用了第一个参数为 exe 文件的地址,第二个参数为传递参数,是我需要传给exe 的字符串。后边主要就是打印 输入流,获取exe 输出信息。其实到这里java 调用exe 就已经完 了,但是后续开发中遇到一种问题,就是程序莫名死锁,没有响应,于是使用debug 跟进代码,发现程序走到 waitfor 代码行的时候程序就出现了挂起的情况,于是google了一番,明白了其中的原因。

2. waitfor 问题描述分析

1.主进程中调用pb.start会创建一个子进程,用于执行shell /exe 脚本。子进程创建后会和主进程分别独立运行。

2. 因为主进程需要等待脚本执行完成,然后对脚本返回值或输出进行处理,所以这里主进程调用Process.waitfor等待子进程完成。

3. 子进程执行过程就是不断的打印信息。主进程中可以通过Process.getInputStream和Process.getErrorStream获取并处理。

4. 这时候子进程不断向主进程发生数据,而主进程调用Process.waitfor后已挂起。当前子进程和主进程之间的缓冲区塞满后,子进程不能继续写数据,然后也会挂起。

5. 这样子进程等待主进程读取数据,主进程等待子进程结束,两个进程相互等待,最终导致死锁。

3. 死锁问题解决

基于上述分析,只要主进程在waitfor之前,能不断处理缓冲区中的数据就可以。因为,我们可以再waitfor之前,单独启两个额外的线程,分别用于处理InputStream和ErrorStream就可以

try {
            //获取进程的标准输入流
            final InputStream is1 = process.getInputStream();
            //获取进城的错误流
            final InputStream is2 = process.getErrorStream();
            //启动两个线程,一个线程负责读标准输出流,另一个负责读标准错误流
            new Thread() {
                public void run() {
                    BufferedReader br1 = new BufferedReader(new InputStreamReader(is1));
                    try {
                        String line1 = null;
                        while ((line1 = br1.readLine()) != null) {
                            if (line1 != null){}
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    finally{
                        try {
                            is1.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }.start();

            new Thread() {
                public void  run() {
                    BufferedReader br2 = new  BufferedReader(new  InputStreamReader(is2));
                    try {
                        String line2 = null ;
                        while ((line2 = br2.readLine()) !=  null ) {
                            if (line2 != null){}
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    finally{
                        try {
                            is2.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }.start();

            //可能导致进程阻塞,甚至死锁
            int ret = process.waitFor();
            System.out.println("return value:"+ret);
            System.out.println(process.exitValue());
            logger.info("event:{}", "RunExeForWindows",process.exitValue());
            byte[] bytes = new byte[process.getInputStream().available()];
            process.getInputStream().read(bytes);
            System.out.println(new String(bytes));
            logger.info("event:{}", "RunExeForWindows",new String(bytes));
        }catch (Exception ex){
            ex.printStackTrace();
            try{
                process.getErrorStream().close();
                process.getInputStream().close();
                process.getOutputStream().close();
            }
            catch(Exception ee){}
        }

如此便可以将 waitfor死锁问题避开,看完这个问题,总结一下,多看官方api注释….其实官方已经提示我们,如下 为 api注释

Causes the current thread to wait, if necessary, until the
     * process represented by this {@code Process} object has
     * terminated.  This method returns immediately if the subprocess
     * has already terminated.  If the subprocess has not yet
     * terminated, the calling thread will be blocked until the
     * subprocess exits.
@return the exit value of the subprocess represented by this
     *         {@code Process} object.  By convention, the value
     *         {@code 0} indicates normal termination.
     * @throws InterruptedException if the current thread is
     *         {@linkplain Thread#interrupt() interrupted} by another
     *         thread while it is waiting, then the wait is ended and
     *         an {@link InterruptedException} is thrown.

如果需要,导致当前线程等待,直到此{@code Process}对象表示的进程具有终止 如果子进程,此方法立即返回已经终止。 如果子进程还没有终止后,调用线程将被阻塞,直到子进程退出。

总结

到此这篇关于java使用Process调用exe程序及Process.waitFor()死锁问题解决的文章就介绍到这了,更多相关java用Process调用exe程序内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解java.lang.reflect.Modifier.isInterface()方法

    详解java.lang.reflect.Modifier.isInterface()方法

    这篇文章主要介绍了详解java.lang.reflect.Modifier.isInterface()方法的相关资料,这里提供实例帮助大家理解这个方法的使用,需要的朋友可以参考下
    2017-09-09
  • Java Synchronize底层原理总结

    Java Synchronize底层原理总结

    这篇文章主要给大家总结了Java Synchronize底层原理,文中的图文讲解介绍的非常详细,对我们学习Java Synchronize有一定的帮助,需要的朋友可以参考下
    2023-06-06
  • IDEA插件之快速删除Java代码中的注释

    IDEA插件之快速删除Java代码中的注释

    这篇文章主要介绍了IDEA插件之快速删除Java代码中的注释,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-02-02
  • java byte数组与16进制间相互转换的示例

    java byte数组与16进制间相互转换的示例

    这篇文章主要介绍了java byte数组与16进制间相互转换的示例,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2020-10-10
  • spring boot实战之内嵌容器tomcat配置

    spring boot实战之内嵌容器tomcat配置

    本篇文章主要介绍了Spring Boot 使用内嵌的tomcat容器配置,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • 解读@NoArgsConstructor,@AllArgsConstructor,@RequiredArgsConstructor的区别及在springboot常用地方

    解读@NoArgsConstructor,@AllArgsConstructor,@RequiredArgsConstr

    这篇文章主要介绍了解读@NoArgsConstructor,@AllArgsConstructor,@RequiredArgsConstructor的区别及在springboot常用地方,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • springboot 如何取消starter的自动注入

    springboot 如何取消starter的自动注入

    这篇文章主要介绍了springboot 如何取消starter的自动注入操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Java实现排球比赛计分系统

    Java实现排球比赛计分系统

    这篇文章主要为大家详细介绍了Java实现排球比赛计分系统,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • Java 单链表数据结构的增删改查教程

    Java 单链表数据结构的增删改查教程

    这篇文章主要介绍了Java 单链表数据结构的增删改查教程,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10
  • Mybatis打印替换占位符后的完整Sql教程

    Mybatis打印替换占位符后的完整Sql教程

    这篇文章主要介绍了Mybatis打印替换占位符后的完整Sql教程,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10

最新评论