問題:使用java調(diào)用shell腳本進行模型訓(xùn)練時疯暑,跑到一半就卡死,單獨執(zhí)行shell腳本則沒有問題缸濒。
因為需求比較簡單,無需等待返回結(jié)果,原執(zhí)行方法如下:
private void exec(String script) throws IOException {
Runtime.getRuntime().exec(script);
}
后進行搜索绍填,找到并修改成如下代碼霎桅,死鎖問題解決。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class CmdExecutor {
public void exec(String cmd) {
try {
Process proc = Runtime.getRuntime().exec(cmd);
GobblerThread errorGobbler = new GobblerThread(proc.getErrorStream(), "ERROR");
GobblerThread outputGobbler = new GobblerThread(proc.getInputStream(), "OUTPUT");
errorGobbler.start();
outputGobbler.start();
proc.waitFor();
logger.info("Exec Algorithm script: {}", cmd);
} catch (Exception e) {
e.printStackTrace();
}
}
class GobblerThread extends Thread {
InputStream is;
String type;
GobblerThread(InputStream is, String type) {
this.is = is;
this.type = type;
}
public void run() {
try {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
原因
JDK幫助文檔上這么說:如有必要讨永,一直要等到由該 Process 對象表示的進程已經(jīng)終止滔驶。如果已終止該子進程,此方法立即返回卿闹。但是直接調(diào)用這個方法會導(dǎo)致當(dāng)前線程阻塞揭糕,直到退出子進程。對此JDK文檔上還有如此解釋:因為本地的系統(tǒng)對標準輸入和輸出所提供的緩沖池有效锻霎,所以錯誤的對標準輸出快速的寫入和從標準輸入快速的讀入都有可能造成子進程的所著角,甚至死鎖。問題的關(guān)鍵在緩沖區(qū)這個地方:可執(zhí)行程序的標準輸出比較多旋恼,而運行窗口的標準緩沖區(qū)不夠大吏口,所以發(fā)生阻塞。
接著來分析緩沖區(qū)冰更,哪來的這個東西,當(dāng)Runtime對象調(diào)用exec(cmd)后产徊,JVM會啟動一個子進程,該進程會與JVM進程建立三個管道連接:標準輸入蜀细,標準輸出和標準錯誤流舟铜。假設(shè)該程序不斷在向標準輸出流和標準錯誤流寫數(shù)據(jù),而JVM不讀取的話奠衔,當(dāng)緩沖區(qū)滿之后將無法繼續(xù)寫入數(shù)據(jù)谆刨,最終造成阻塞在waitfor()這里。 因此網(wǎng)上的解決方法是開兩個線程在waitfor()命令之前讀出窗口的標準輸出緩沖區(qū)和標準錯誤流的內(nèi)容归斤。