关于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执行Cmd命令出现的死锁问题解决的更多相关文章
- java执行cmd命令并获取输出结果
1.java执行cmd命令并获取输出结果 import java.io.BufferedReader; import java.io.InputStreamReader; import org.apa ...
- 如何使用Java执行cmd命令
用JAVA代码实现执行CMD命令的方法! Runtime rt = Runtime.getRuntime(); Process p = rt.exec(String[] cmdarray); ...
- Java执行cmd命令、bat脚本、linux命令,shell脚本等
1.Windows下执行cmd命令 如复制 D:\tmp\my.txt 到D:\tmp\my_by_only_cmd.txt 现文件如图示: 执行代码: private static void run ...
- 解决方案--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 ...
- java执行cmd命令和linux命令
文章出处http://blog.csdn.net/xh16319/article/details/17302947 一:window下执行cmd指定 一:window下执行cmd指定 程序例子: [j ...
- JAVA执行cmd命令方法
package com.cmd; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStre ...
- Java执行CMD命令
参见:https://blog.csdn.net/lixingshi/article/details/50467840 public static void runtimeCommand() thro ...
- Java执行cmd命令工具类
工具类: public class CmdTask implements Runnable { private String command; private String dirPath; publ ...
- java中执行cmd命令
一.java执行cmd命令的三种方式:http://www.jb51.net/article/80829.htm 参考:https://www.cnblogs.com/zhufu9426/p/7928 ...
- 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 ...
随机推荐
- 使用symbolicatecrash工具符号化Crash日志
对于打包上线的APP,或者打包测试的APP,出现了崩溃并不能方便的把手机链接到电脑使用XCode自动符号化,此时手动符号化就是重要的选项. 1.查找符号化工具symbolicatecrash find ...
- ElasticSearch7.3学习(四)----结合Spring boot进行增删改查和批量(bulk)详解
1.前置 java api 文档 https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.3/java-rest-overvi ...
- Window Server+IIS配置实现一台服务器绑定多个HTTPS证书
参考原文链接:https://blog.csdn.net/lengyiqiu/article/details/89182239 此处做个记录防止丢失: 直接上步骤: 1.选安装好SSL证书,供下面配置 ...
- MySQL执行函数时报错:Illegal mix of collations (utf8mb4_general_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT) for operation 'find_in_set'
执行函数时报错: Illegal mix of collations (utf8mb4_general_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT) f ...
- Apache Hudi 设计与架构解读
1. 简介 Apache Hudi(简称:Hudi)允许您在现有的hadoop兼容存储之上存储大量数据,同时提供两种原语,使得除了经典的批处理之外,还可以在数据湖上进行流处理. 这两种原语分别是: 1 ...
- burpsuit+adb+逍遥模拟器
安卓7之后,单纯的将burpsuit的证书导出手动安装到模拟器中已经不行了,app可以只信任指定证书和系统内置的证书,后续用户安装的证书是不生效的,只能想办法装到系统内部 需要将证书通过openssl ...
- 【Unity3D】2D动画
1 图片处理 通过 PS 软件将以下 gif 文件中的黑色背景删除,并将其中的 18 个图层分别保存为 png 格式图片. 2 游戏对象 1)游戏对象层级结构 2)Transform组件参 ...
- html中iframe调用兄弟iframe中的js方法
问题说明 最近工作中碰到一个页面有一个主iframe A,用于操作主要业务元素.其中有一个弹出框里面也嵌入了一个iframe B, 此时,我需要在B中调用A中JS的指定方法.下面咱们来通过例子还原一下 ...
- spring事务的传播
目录 事务的传播行为类型 注意事项 关于事务的传播,我们先确定一个场景:方法A调用方法B,方法A可能存在事务,也可能不存在事务,我们这里重点关注方法B上定义的事务传播行为,以及方法B中出现异常时,方法 ...
- Go语言并发编程(2):channel 通道介绍和使用
一.简介 channel 不仅可以用于 goroutine 间进行安全通信,还可以用于同步内存访问. 而且 Go 社区强烈推荐使用 channel 通道实现 goroutine 之间的通信, 不要通过 ...