解决Java执行Cmd命令出现的死锁问题
作者:Stars-one 时间:2023-06-15 09:54:56
问题
之前研究了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类的使用与注意事项说明
来源:https://www.cnblogs.com/stars-one/p/16482964.html
标签:Java,Cmd命令,死锁
0
投稿
猜你喜欢
执行java请求时导致在脚本执行结束时JVM无法退出
2023-11-25 05:57:13
idea 多模块项目依赖父工程class找不到问题的方法
2022-06-08 10:47:41
C# pictureBox用法案例详解
2022-02-24 19:40:07
java插入排序 Insert sort实例
2023-07-21 07:07:37
springmvc之获取参数的方法(必看)
2023-12-20 09:25:59
浅析Android 快速实现图片压缩与上传功能
2022-10-15 23:58:01
SpringBoot设置编码UTF-8的两种方法
2022-05-04 00:09:08
Java编程中的4种代码块详解
2022-01-04 03:10:20
Android简单实现文件下载
2023-08-28 06:33:17
Spring MVC 简单的hello world的实现
2023-06-18 02:56:41
mybatis-plus自动生成代码的示例代码
2023-08-04 22:38:32
Java设计模式之单例和原型
2023-11-29 04:14:18
浅析MMAP零拷贝在RocketMQ中的运用
2021-11-21 01:59:47
spring boot加载第三方jar包的配置文件的方法
2023-03-02 22:45:13
Java中File文件操作类的基础用法
2022-09-28 14:37:03
Android中执行java命令的方法及java代码执行并解析shell命令
2022-08-27 15:45:13
Java注解与反射原理说明
2021-06-18 01:56:00
Nacos源码之注册中心的实现详解
2023-04-30 19:25:49
轻松学习C#的异常处理
2022-09-14 22:10:20
简单介绍java中equals以及==的用法
2023-01-28 07:47:41