ProcessBuilder pb = new ProcessBuilder("C:\\Debug\\TestRedis.exe", keyNmae); pb.redirectErrorStream(true); Process process = pb.start(); //可能導致進程阻塞,甚至死鎖 int ret = process.waitFor();
1、waitFor問題描述分析
1、 主進程中調用pb.start會創建一個子進程,用於執行shell /exe 腳本。子進程創建后會和主進程分別獨立運行。
2.、因為主進程需要等待腳本執行完成,然后對腳本返回值或輸出進行處理,所以這里主進程調用process.waitFor()等待子進程完成。
3.、子進程執行過程就是不斷的打印信息。主進程中可以通過Process.getInputStream和Process.getErrorStream獲取並處理。
4.、這時候子進程不斷向主進程發生數據,而主進程調用Process.waitfor后已掛起。當前子進程和主進程之間的緩沖區塞滿后,子進程不能繼續寫數據,然后也會掛起。
5.、這樣子進程等待主進程讀取數據,主進程等待子進程結束,兩個進程相互等待,最終導致死鎖。
2、解決死鎖問題
基於上述分析,只要主進程在waitFor之前,能不斷處理緩沖區中的數據就可以。因為,我們可以再waitfor之前,單獨啟2個額外的線程,分別用於處理InputStream和ErrorStream就可以
try ( InputStream inputStream = process.getInputStream(); InputStream inputErrorStream = process.getErrorStream(); ) { // 讀取Shell的輸出內容,並添加到string中: //啟動兩個線程,一個線程負責讀標准輸出流,另一個負責讀標准錯誤流 new Thread(() -> { try { val msg = IOUtils.toString(inputStream, StandardCharsets.UTF_8); response.setShellOutput(msg); } catch (Exception e) { log.error(e.getMessage(), e); } }).start(); new Thread(() -> { try { var errMsg = IOUtils.toString(inputErrorStream, StandardCharsets.UTF_8); response.setShellErrorOutput(errMsg); } catch (Exception e) { log.error(e.getMessage(), e); } }).start(); process.waitFor(2, TimeUnit.HOURS);////可能導致進程阻塞,甚至死鎖 int exitCode = process.exitValue();// response.setSuccessful(exitCode == SHELL_EXIT_CODE_SUCCESS); }