简体   繁体   中英

BufferedReader.readLine blocks my program but BufferedReader.read() reads properly

I have a snippet as follows:

Process proc = Runtime.getRuntime().exec(command);
BufferedReader br = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
String line = br.readLine();

Now in the above code I am sure that process will always have on line input, so I did not use any kind of while loop or any null check. The problem is readLine blocks. The one reason I was aware of is, the stream having no data to read and hence readLine keeps waiting. To check this, I removed readLine and used read() function as follows:

Process proc = Runtime.getRuntime().exec( command );
BufferedReader br = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
int a;
while((a=br.read())!=-1){
    char ch = (char) a;
    if(ch == '\n')
        System.out.print("New line "+ch);
    if(ch == '\r')
        System.out.print("Carriage return "+ch);
    System.out.print(ch);
}

To my surprise this code worked and printed the messags New line and Carriage return. Now I am wondering why did the readLine block? The data is available it is terminated by newline. What else could be the reason??

Note: The above worked once in a while! Maybe once out of 15times.
Note: I tried using ProcessBuilder too, but same behaviour.

UPDATE: So I switched to ProcessBuilder and then I redirected the errorStream and now I get both input stream and error stream at once when I do process.getInputStream and this works fine. Below is the snippet.

ProcessBuilder pb = new ProcessBuilder(command.split(" "));
pb..redirectErrorStream(true);
Process proc = pb.start();
BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line = br.readLine();
//Now I get both input and error stream.

I would like to differentiate my error stream from input stream but with this method they are all jumbled up! Any ideas on this?

You can use threads, in order to avoid it.

Like One slave thread which will be responsible for reading. This will not halt your progress of program.

I think the problem is not that the standard error is blocking, but that the standard output is blocking causing the application you are calling to block.

Standard output is normally buffered. If the process you are calling writes less to standard output than the buffer size all is well and it can reach the code that writes to standard error. If the process fills the buffer, its attempt to write to standard output will block and it will never reach the point where it writes to standard error.

This could be why you see it working occasionally - sometimes the standard output does not fill the buffer. It could also be why it works after a long time: eventually the write to standard output times out.

As a demonstration, this simple process always block like you describe on my Windows 8 machine:

public class Proc {

    public static void main(String[] args) {
        for(int i=0;i<1000;i++) {
            System.out.print("More data ");
        }
        System.out.println();
        System.err.println("An error line");
    }
}

So to avoid getting the error stream and input stream merged just remove the line pb.redirectErrorStream(true);

Because as said in the java doc :

if this property is true, then any error output generated by subprocesses subsequently started by this object's start() method will be merged with the standard output, so that both can be read using the Process.getInputStream() method. This makes it easier to correlate error messages with the corresponding output. The initial value is false.

By calling pb.redirectErrorStream(true); you are merging both output.

getErrorStream states the following Returns the input stream connected to the error output of the subprocess. The stream obtains data piped from the error output of the process represented by this Process object. If the standard error of the subprocess has been redirected using ProcessBuilder.redirectError or ProcessBuilder.redirectErrorStream then this method will return a null input stream.

and the ReadLine states the following Reads a line of text. A line is considered to be terminated by any one of a line feed ('\\n'), a carriage return ('\\r'), or a carriage return followed immediately by a linefeed.

Based on the explanation provided to the API. readline waits indefinitely to get line feed or carriage return where as processbuilder returns NULL that is why readLine terminates for the same.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM