原文:关于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

参考

关于Java执行Cmd命令出现的死锁问题解决的更多相关文章

  1. java执行cmd命令并获取输出结果

    1.java执行cmd命令并获取输出结果 import java.io.BufferedReader; import java.io.InputStreamReader; import org.apa ...

  2. 如何使用Java执行cmd命令

    用JAVA代码实现执行CMD命令的方法! Runtime rt = Runtime.getRuntime(); Process p = rt.exec(String[] cmdarray);     ...

  3. Java执行cmd命令、bat脚本、linux命令,shell脚本等

    1.Windows下执行cmd命令 如复制 D:\tmp\my.txt 到D:\tmp\my_by_only_cmd.txt 现文件如图示: 执行代码: private static void run ...

  4. 解决方案--java执行cmd命令ProcessBuilder--出错Exception in thread "main" java.io.IOException: Cannot run program "dir d:\": CreateProcess error=2(xjl456852原创)

    当我尝试在java中通过ProcessBuilder运行window的cmd命令时出现错误: public static void main(String [] args) throws IOExce ...

  5. java执行cmd命令和linux命令

    文章出处http://blog.csdn.net/xh16319/article/details/17302947 一:window下执行cmd指定 一:window下执行cmd指定 程序例子: [j ...

  6. JAVA执行cmd命令方法

    package com.cmd; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStre ...

  7. Java执行CMD命令

    参见:https://blog.csdn.net/lixingshi/article/details/50467840 public static void runtimeCommand() thro ...

  8. Java执行cmd命令工具类

    工具类: public class CmdTask implements Runnable { private String command; private String dirPath; publ ...

  9. java中执行cmd命令

    一.java执行cmd命令的三种方式:http://www.jb51.net/article/80829.htm 参考:https://www.cnblogs.com/zhufu9426/p/7928 ...

  10. Java 调用并执行cmd命令

    cmd java 调用 执行 概要: Java 调用并执行cmd命令 Java | 复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 2 ...

随机推荐

  1. element实现大图预览和图片动态加载

    element的el-image组件支持大图预览模式,但需要和小图模式配合使用,项目中刚好有需求需要直接使用大图预览并且需要支持图片的动态加载,研究了一下el-image组件的源码发现el-image ...

  2. AI PC两年要大卖1亿台!就靠它了

    Intel在中国北京召开了主题为"AI无处不在,创芯无所不及"的2023Intel新品发布会暨AI技术创新派对,正式发布了代号为"Meteor Lake"的面向 ...

  3. MySQL-顺序IO和随机IO的区别

    顺序IO是指读写操作的访问地址连续.在顺序IO访问中,HDD所需的磁道搜索时间显着减少,因为读/写磁头可以以最⼩的移动访问下一个块.数据备份和日志记录等业务是顺序IO业务.随机IO是指读写操作时间连续 ...

  4. 【译】发布 .NET Aspire 预览版 2(一)

    原文 | Damian Edwards 翻译 | 郑子铭 自上个月宣布并推出 .NET Aspire 以来,我们收到的反馈非常惊人!通过问题和拉取请求对回购协议的参与一直激励着团队.我们正在深入了解开 ...

  5. NEMU PA 4 实验报告

    一.实验目的 在前面的PA123中,我们分别实现了基本的运算单元,实现了各种指令和程序的装载,实现了存储器的层次结构.而在PA4中,为了让NEMU可以处理异常情况以及和外设交互,我们要做的事情有以下: ...

  6. NC17871 CSL分苹果

    题目链接 题目 题目描述 CSL手上有n个苹果,第i个苹果的质量是wi,现在他想把这些苹果分给他的好朋友wavator和tokitsukaze.但是CSL为了不让他们打架,根据质量决定尽量地均分成两堆 ...

  7. powerdesigner自定义实体显示的属性

    我做概要设计的时候需要画实体的逻辑模型图,默认的时候是这样的: 但是我想只保留属性名,隐藏数据类型和下面的横线怎么办?效果如下: 按以下操作即可实现:

  8. 循环掌控:深入理解C语言循环结构,高效实现重复性任务

    欢迎大家来到贝蒂大讲堂 养成好习惯,先赞后看哦~ 所属专栏:C语言学习 贝蒂的主页:Betty's blog 引言 前面贝蒂带大家了解了选择结构,今天就来为大家介绍循环结构,也就是我们熟悉的while ...

  9. SpringBoot的自动装配原理及应用

    什么是SpringBoot自动装配 所谓的"SpringBoot自动装配"就是指:通过注解和一些简单的配置就能将某些组件载入Spring容器环境中,便于使用. 比如,很多sprin ...

  10. mysql中如何批量生成百万级数据

    # 准备 #1. 准备表 create table s1( id int, name varchar(20), gender char(6), email varchar(50), first_nam ...