解決Java執(zhí)行Cmd命令出現(xiàn)的死鎖問題
問題
之前研究了Java通過執(zhí)行cmd命令從而觸發(fā)Android打包的思路,但是發(fā)現(xiàn)Android打包成功之后,后面的代碼邏輯就不走了(連輸出都沒有)
經(jīng)過了一天的排查,終于是從網(wǎng)上找到了解決方法
原因及解決方法
原因分析: 在上面提及了, process創(chuàng)建的子進(jìn)程沒有自己的控制臺或終端,其所有的io操作都是通過(輸入流、輸出流、錯(cuò)誤流)重定向到父進(jìn)程中
如果該可執(zhí)行程序的輸入、輸出或者錯(cuò)誤輸出比較多的話,而由于運(yùn)行窗口的標(biāo)準(zhǔn)輸入、輸出等緩沖區(qū)有大小的限制,則可能導(dǎo)致子進(jìn)程阻塞,甚至產(chǎn)生死鎖
其解決方法就是在waitfor()
方法之前讀出窗口的標(biāo)準(zhǔn)輸出、輸出、錯(cuò)誤緩沖區(qū)中的內(nèi)容。
方法封裝
下面代碼中的TeeInputStream是在lang3包依賴中,記得添加依賴
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency>
Java版本:
/** * 執(zhí)行命令行,并等待命令執(zhí)行完畢,同時(shí)將過程中的控制臺輸出日志寫入日志文件中 * @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(); //開啟兩個(gè)線程用來讀取流,否則會造成死鎖問題 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("流錯(cuò)誤:" + errline); } } catch (Exception e) { e.printStackTrace(); } finally { try { bferr.close(); err.close(); } catch (IOException e) { e.printStackTrace(); } } }).start(); process.waitFor(); process.destroy(); }
Kotlin版本:
/** * 執(zhí)行命令行,并等待命令執(zhí)行完畢,同時(shí)將過程中的控制臺輸出日志寫入日志文件中 * - [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 //開啟兩個(gè)線程用來讀取流,否則會造成死鎖問題 thread { var fileOutputStream: FileOutputStream? = null var teeInputStream: TeeInputStream? = null var bufferedReader: BufferedReader? = null try { fileOutputStream = FileOutputStream(logFile, true) //使用分流器,日志文件和 teeInputStream = TeeInputStream(inputStream, fileOutputStream) //區(qū)分不同平臺 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("流錯(cuò)誤:$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類的使用與注意事項(xiàng)說明
到此這篇關(guān)于關(guān)于Java執(zhí)行Cmd命令出現(xiàn)的死鎖問題解決的文章就介紹到這了,更多相關(guān)Java執(zhí)行Cmd命令死鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中將UUID存儲為Base64字符串的方法實(shí)現(xiàn)
使用Base64編碼來對UUID存儲在一些特定的場合被廣泛的使用,本文主要介紹了Java中將UUID存儲為Base64字符串的方法實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2024-04-04Java可以如何實(shí)現(xiàn)文件變動的監(jiān)聽的示例
本篇文章主要介紹了Java可以如何實(shí)現(xiàn)文件變動的監(jiān)聽的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-02-02詳解Spring Boot中使用@Scheduled創(chuàng)建定時(shí)任務(wù)
本篇文章中主要介紹了Spring Boot中使用@Scheduled創(chuàng)建定時(shí)任務(wù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03Java實(shí)現(xiàn)計(jì)算機(jī)程序設(shè)計(jì)思路
這篇文章主要為大家介紹了Java實(shí)現(xiàn)計(jì)算機(jī)程序設(shè)計(jì)思路,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11Java Swing組件下拉菜單控件JComboBox用法示例
這篇文章主要介紹了Java Swing組件下拉菜單控件JComboBox用法,結(jié)合具體實(shí)例形式分析了Swing組件下拉菜單控件JComboBox的具體定義、使用方法及相關(guān)使用注意事項(xiàng),需要的朋友可以參考下2017-11-11