preface

Recently, while developing Android and Java, I encountered the need to use Java program to call EXE, here I use process to call. It takes 8+ minutes to read this article, the article type is small white entry type, here is the main record, for the convenience of later learning supplement… If there is an incorrect place, please note….

Refer to the Process reference

Waitfor suspends parsing

1. Use process to invoke the EXE program


ProcessBuilder pb = new ProcessBuilder("C:\\Debug\\TestRedis.exe", keyNmae);
pb.redirectErrorStream(true);
Process process = pb.start();
// May cause process to block or even deadlock
int ret = process.waitFor();
System.out.println("return value:"+ret);
System.out.println(process.exitValue());
byte[] bytes = new byte[process.getInputStream().available()];
process.getInputStream().read(bytes);
System.out.println(new String(bytes));
Copy the code

// ProcessBuilder API method
public ProcessBuilder(String... command) {
        this.command = new ArrayList<>(command.length);
        for (String arg : command)
            this.command.add(arg);
    }
Copy the code

The first argument is the address of the EXE file, and the second argument is the string THAT I need to pass to the EXE file. After the main is to print the input stream, get the OUTPUT information of EXE. In fact, at this point Java has finished calling exe, but later in the development encountered a problem, is the program inexplicably deadlock, no response, so use debug to follow up the code, found that the program went to the waitfor line of code when the program hung, so Google, understand the reason.

2. Waitfor problem description and analysis

1. Calling pb.start in the main process will create a child process to execute shell /exe scripts. After the child process is created, it runs independently from the main process. 2. Since the main Process needs to waitfor the completion of the script execution and then Process the returned value or output of the script, the main Process calls process. waitfor to waitfor the completion of the child Process. 3. The execution process of the child process is to print information constantly. The main Process can be obtained and processed by process. getInputStream and process. getErrorStream. 4. At this time, the child Process continues to generate data to the main Process, and the main Process has been suspended after calling process. waitfor. When the buffer between the current child process and the main process fills up, the child process cannot continue to write data, and then it also hangs. 5. In this way, the process waits for the main process to read data, the main process waits for the child process to finish, and the two processes wait for each other, resulting in deadlock.

3. The deadlock problem is resolved

Based on the above analysis, as long as the main process can continuously process the data in the buffer before waitfor. Because we can start two additional threads before waitfor, one for InputStream and one for ErrorStream


try {
            // Get the process's standard input stream
            final InputStream is1 = process.getInputStream();
            // Get the incoming error stream
            final InputStream is2 = process.getErrorStream();
            // Start two threads, one for reading the standard output stream and the other for reading the standard error stream
            new Thread() {
                public void run(a) {
                    BufferedReader br1 = new BufferedReader(new InputStreamReader(is1));
                    try {
                        String line1 = null;
                        while((line1 = br1.readLine()) ! =null) {
                            if(line1 ! =null) {}}}catch (IOException e) {
                        e.printStackTrace();
                    }
                    finally{
                        try {
                            is1.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }.start();

            new Thread() {
                public void  run(a) {
                    BufferedReader br2 = new  BufferedReader(new  InputStreamReader(is2));
                    try {
                        String line2 = null ;
                        while((line2 = br2.readLine()) ! =null ) {
                            if(line2 ! =null) {}}}catch (IOException e) {
                        e.printStackTrace();
                    }
                    finally{
                        try {
                            is2.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }.start();

            // May cause process to block or even deadlock
            int ret = process.waitFor();
            System.out.println("return value:"+ret);
            System.out.println(process.exitValue());
            logger.info("event:{}"."RunExeForWindows",process.exitValue());
            byte[] bytes = new byte[process.getInputStream().available()];
            process.getInputStream().read(bytes);
            System.out.println(new String(bytes));
            logger.info("event:{}"."RunExeForWindows".new String(bytes));
        }catch (Exception ex){
            ex.printStackTrace();
            try{
                process.getErrorStream().close();
                process.getInputStream().close();
                process.getOutputStream().close();
            }
            catch(Exception ee){}
        }
Copy the code

This will avoid the problem of waitfor deadlock. After looking at this issue, to summarize, take a look at the official API comment…. In fact, the official has prompted us, the following API comments


Causes the current thread to wait, if necessary, until the
     * process represented by this {@code Process} object has
     * terminated.  This method returns immediately if the subprocess
     * has already terminated.  If the subprocess has not yet
     * terminated, the calling thread will be blocked until the
     * subprocess exits.
@return the exit value of the subprocess represented by this
     *         {@code Process} object.  By convention, the value
     *         {@code 0} indicates normal termination.
     * @throws InterruptedException if the current thread is
     *         {@linkplain Thread#interrupt() interrupted} by another
     *         thread while it is waiting, then the wait is ended and
     *         an {@link InterruptedException} is thrown.
Copy the code

If required, causes the current thread to wait until the Process represented by this {@code Process} object has terminated if the child Process, this method immediately returns terminated. If the child has not been terminated, the calling thread will be blocked until the child exits.

July 3, 2018 at 11:22:55

-- Shanghai BaymaxCopy the code