Process.waitFor() 死鎖問題了解和解決
阿新 • • 發佈:2020-07-28
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); }