解决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
  • asp之家 软件编程 m.aspxhome.com