ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

关于Java执行Cmd命令出现的死锁问题解决

2022-07-15 22:37:17  阅读:157  来源: 互联网

标签:Cmd Java process cmd 死锁 teeInputStream catch close null


原文:关于Java执行Cmd命令出现的死锁问题解决 - Stars-One的杂货小窝

问题

之前研究了Java通过执行cmd命令从而触发Android打包的思路,但是发现Android打包成功之后,后面的代码逻辑就不走了(连输出都没有)

经过了一天的排查,终于是从网上找到了解决方法

原因及解决方法

原因分析: 在上面提及了, process创建的子进程没有自己的控制台或终端,其所有的io操作都是通过(输入流、输出流、错误流)重定向到父进程中

如果该可执行程序的输入、输出或者错误输出比较多的话,而由于运行窗口的标准输入、输出等缓冲区有大小的限制,则可能导致子进程阻塞,甚至产生死锁

其解决方法就是在waitfor()方法之前读出窗口的标准输出、输出、错误缓冲区中的内容。

方法封装

下面代码中的TeeInputStream是在lang3包依赖中,记得添加依赖

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.6</version>
</dependency>

Java版本:

/**
 *  执行命令行,并等待命令执行完毕,同时将过程中的控制台输出日志写入日志文件中
 * @param cmd 命令,window记得要使用cmd /c开头,如cmd /c ipconfig
 * @param dir 命令行所在路径
 * @param logFile 日志文件
 * @throws IOException
 * @throws InterruptedException
 */
private void execCmdLine(String cmd, File dir, File logFile) throws IOException, InterruptedException {
    Process process = Runtime.getRuntime().exec(cmd, null, dir);
    InputStream inputStream = process.getInputStream();

    //开启两个线程用来读取流,否则会造成死锁问题
    new Thread(() -> {
        FileOutputStream fileOutputStream = null;
        TeeInputStream teeInputStream = null;
        BufferedReader bufferedReader = null;
        try {
            fileOutputStream = new FileOutputStream(logFile, true);
            //使用分流器,输出日志文件
            teeInputStream = new TeeInputStream(inputStream, fileOutputStream);
            //这里gbk格式需要注意,我是在window上测试的,所以使用是gbk方式,如果是其他平台,可能需要使用utf-8格式
            bufferedReader = new BufferedReader(new InputStreamReader(teeInputStream, "gbk"));
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                bufferedReader.close();
                teeInputStream.close();
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }).start();
    new Thread(() -> {
        InputStreamReader err = new InputStreamReader(process.getErrorStream());
        BufferedReader bferr = new BufferedReader(err);
        String errline = "";
        try {
            while ((errline = bferr.readLine()) != null) {
                System.out.println("流错误:" + errline);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                bferr.close();
                err.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start();
    process.waitFor();
    process.destroy();
}

Kotlin版本:

/**
 * 执行命令行,并等待命令执行完毕,同时将过程中的控制台输出日志写入日志文件中
 * - [cmd] 命令,window记得要使用cmd /c开头,如cmd /c ipconfig
 * - [dir] 命令行所在路径
 * - [logFile] 日志文件
 */
 fun execCmd(cmd: String, dir: File, logFile: File) {
    val process = Runtime.getRuntime().exec(cmd, null, dir)
    val inputStream = process.inputStream

    //开启两个线程用来读取流,否则会造成死锁问题
    thread {
        var fileOutputStream: FileOutputStream? = null
        var teeInputStream: TeeInputStream? = null
        var bufferedReader: BufferedReader? = null
        try {
            fileOutputStream = FileOutputStream(logFile, true)
            //使用分流器,日志文件和
            teeInputStream = TeeInputStream(inputStream, fileOutputStream)
            //区分不同平台
            bufferedReader = if (isWin()) {
                BufferedReader(InputStreamReader(teeInputStream, "gbk"))
            } else {
                BufferedReader(InputStreamReader(teeInputStream, "utf-8"))
            }
            var line: String?
            while (bufferedReader.readLine().also { line = it } != null) {
                println(line)
            }
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            try {
                bufferedReader!!.close()
                teeInputStream!!.close()
                fileOutputStream!!.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
    }
    thread {
        val err = InputStreamReader(process.errorStream)
        val bferr = BufferedReader(err)
        var errline = ""
        try {
            while (bferr.readLine().also { errline = it } != null) {
                println("流错误:$errline")
            }
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            try {
                bferr.close()
                err.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
    }
    process.waitFor()
    process.destroy()
}

代码封装在库中stars-one/common-controls: TornadoFx的常用控件 controls for tornadofx

参考

标签:Cmd,Java,process,cmd,死锁,teeInputStream,catch,close,null
来源: https://www.cnblogs.com/stars-one/p/16482964.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有