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

目录
  • 问题
  • 原因及解决方法
  • 方法封装
  • 参考

问题

之前研究了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

参考

Java中 Process类的使用与注意事项说明

到此这篇关于关于Java执行Cmd命令出现的死锁问题解决的文章就介绍到这了,更多相关Java执行Cmd命令死锁内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java执行cmd命令的举例与注意事项

    目录 eg1: 执行命令 eg2: 执行命令,并获取正常输出与错误输出 值得注意的是: 附java调用cmd命令实现各种操作 总结 通常 Java 执行 Windows 或者 Linux 的命令时,都是使用 Runtime.getRuntime.exec(command) 来执行的 eg1: 执行命令 public static void execCommand() { try { Runtime runtime = Runtime.getRuntime(); // 打开任务管理器,exec方法

  • Java调用windows系统的CMD命令并启动新程序

    我们通常在使用Java 调用脚本的时候,会使用 Runtime 类如: // 打开浏览器并访问 http://localhost:7001 Runtime.getRuntime().exec("cmd /c start http://localhost:8080"); 这里我们使用到了终端 CMD # 执行字符串指定的命令,并在执行命令后保留窗口 cmd /k echo "Hello World!" # 执行字符串指定的命令,然后退出 cmd /c echo &qu

  • 解决java执行cmd命令调用ffmpeg报错Concat error - No such filter '[0,0]'问题

    最近公司有一个公交项目,要生成报站语音,采用的是 报站前缀 + 站点名 + 报站后缀,3个MP3文件拼接的方式,拼接成一个完整的语音,且需要转码率和比特率,使用到了ffmpeg. 去网上找了一些ffmpeg相关的命令,参考: 安装命令参考 音频转码: ffmpeg -i **.mp3 -ar 8000 -ac 2 -y -b:a 32k -vol 400 **.mp3 音频拼接: //音频拼接 (1) ffmpeg -i "concat:test4.mp3|180_out.mp3" -

  • Java 执行CMD命令或执行BAT批处理方式

    Java 执行CMD命令或执行BAT批处理 背景 日常开发中总能遇到一些奇怪的需求,例如使用java执行cmd命令或者bat批处理文件,今天就简单记录一下使用过程. 使用 废话不多说直接上代码 import java.io.BufferedReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; public class Cmder { /** * 执行一个cm

  • Java执行cmd命令两种实现方法解析

    一般java在执行CMD命令时,通常是使用Runtime.getRuntime.exec(command)来执行的,这个方法有两种细节要注意: 1.一般执行方法,代码如下,这种方法有时执行exe时会卡在那里. //一般的执行方法,有时执行exe会卡在那 stmt要执行的命令 public static void executive(String stmt) throws IOException, InterruptedException { Runtime runtime = Runtime.g

  • Java如何执行cmd命令

    目录 Java执行cmd命令 Java执行cmd命令总结 解释一下参数 总结 Java执行cmd命令 //当前绝对路径 System.out.println(IoUtil.read(Runtime.getRuntime().exec("cmd /c echo %cd%").getInputStream(), Charset.forName("gbk"))); //创建文件夹 System.out.println(IoUtil.read(Runtime.getRunt

  • Windows系统中Java调用cmd命令及执行exe程序的方法

    Java调用cmd命令,并输出显示信息: package com.anxin.cmd.test; import java.io.BufferedReader; import java.io.InputStreamReader; public class Command { public static void main(String[] args) { try { Runtime rt = Runtime.getRuntime(); Process pr = rt.exec("cmd /c di

  • java执行Linux命令的方法

    本文实例讲述了java执行Linux命令的方法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: public class StreamGobbler extends Thread {            InputStream is;      String type;        public StreamGobbler(InputStream is, String type) {          this.is = is;          this.type = ty

  • python隐藏终端执行cmd命令的方法

    在用pyinstaller打包后不想要后面的终端命令框,但是打包时加了-w或者--noconsole命令后会导致cmd程序不能运行从而出错.这个时候用subprocess可以解决该类问题. import subprocess cmd = 'your command' res = subprocess.call(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 这样打包后出

  • python打包exe文件并隐藏执行CMD命令窗口问题

    目录 python打包exe文件并隐藏执行CMD命令窗口 Python打包exe程序避坑指南:没有安装包也能运行小程序 如果有问题看下面 总结 python打包exe文件并隐藏执行CMD命令窗口 虚拟环境安装 pyinstaller pip install pyinstaller  打包exe命令:(具体的命令网上资料很多) # 打包1个py文件,并隐藏执行窗口 pyinstaller -F -w main.py    # 打包1个py文件(-F),并隐藏执行窗口(-w),替换exe的ico图标

  • PHP中执行cmd命令的方法

    本文介绍下,在php代码中执行cmd命令的方法,介绍下在php.ini文件中配置safe_mode参数支持命令执行的方法,有需要的朋友参考下. 说明: 本节内容在wamp包安装的环境实现. 首先,打开php.ini,关掉安全模式safe_mode = off,然后在看看 禁用函数列表 disable_functions = proc_open, popen, exec, system, shell_exec ,把exec去掉. php代码: 复制代码 代码如下: <?php exec("m

随机推荐