Runtime.getRuntime(). Exec (cmd) висит

Я выполняю команду, которая возвращает мне номер версии файла; 'имя файла'. Но если есть какая-то проблема с выполнением команды, приложение приостанавливается. Что я могу сделать, чтобы избежать этого условия? Ниже вы найдете мой код.

String cmd= "cmd /C si viewhistory --fields=revision --project="+fileName; 
Process p = Runtime.getRuntime().exec(cmd) ;  
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));  
String line = null; 
while ((line = in.readLine()) != null) {  
System.out.println(line);  
} 

} catch (Exception e) {  
e.printStackTrace();  
 }

Ответ 1

Я думаю, проблема в том, что вы читаете только InputStream и не читаете ErrorStream. Вы также должны следить за тем, чтобы оба потока считывались параллельно. Может случиться так, что в настоящее время данные, поступающие из выходного потока, заполняют буфер ОС, ваша команда exec автоматически приостанавливается, чтобы дать вашему читателю возможность очистить буфер. Но программа все равно будет ждать обработки процесса. Следовательно, происходит зависание.

Вы можете создать отдельный класс для обработки как потока ввода, так и потока ошибок следующим образом:

public class ReadStream implements Runnable {
    String name;
    InputStream is;
    Thread thread;      
    public ReadStream(String name, InputStream is) {
        this.name = name;
        this.is = is;
    }       
    public void start () {
        thread = new Thread (this);
        thread.start ();
    }       
    public void run () {
        try {
            InputStreamReader isr = new InputStreamReader (is);
            BufferedReader br = new BufferedReader (isr);   
            while (true) {
                String s = br.readLine ();
                if (s == null) break;
                System.out.println ("[" + name + "] " + s);
            }
            is.close ();    
        } catch (Exception ex) {
            System.out.println ("Problem reading stream " + name + "... :" + ex);
            ex.printStackTrace ();
        }
    }
}

Как вы его используете,

String cmd= "cmd /C si viewhistory --fields=revision --project="+fileName; 
Process p = Runtime.getRuntime().exec(cmd) ;  
s1 = new ReadStream("stdin", p.getInputStream ());
s2 = new ReadStream("stderr", p.getErrorStream ());
s1.start ();
s2.start ();
p.waitFor();        
} catch (Exception e) {  
e.printStackTrace();  
} finally {
    if(p != null)
        p.destroy();
}

Ответ 2

Этот код основан на том же решении Arham, но реализован с использованием параллельного потока java 8, что делает его немного более кратким.

public static String getOutputFromProgram(String program) throws IOException {
    Process proc = Runtime.getRuntime().exec(program);
    return Stream.of(proc.getErrorStream(), proc.getInputStream()).parallel().map((InputStream isForOutput) -> {
        StringBuilder output = new StringBuilder();
        try (BufferedReader br = new BufferedReader(new InputStreamReader(isForOutput))) {
            String line;
            while ((line = br.readLine()) != null) {
                output.append(line);
                output.append("\n");
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return output;
    }).collect(Collectors.joining());
}

Вы можете вызвать метод, подобный этому

getOutputFromProgram("cmd /C si viewhistory --fields=revision --project="+fileName);

Обратите внимание, что этот метод зависает, если зависает программа, которую вы вызываете, что произойдет, если она потребует ввода.