The main program crashes when the output of processB is read, which starts another processC that runs forever

There are three Java applications, appB just launches an appC that will last forever. appA launches appB and reads its output. But appA never comes out. Anyone has an idea why this is happening and how to solve it. It does not hang unless I read the stream in appA.

I tried two ways:

  • Direct access to the read stream (readViaInputStream) or BufferedReader (readViaBufferedReader).

They do not work, the output will be:

// if we call them, the main application freezes, the output will be:

// Before calling the child process

// After calling the child process

// the main program hangs here.

// Never output - "Stream reading completed."

In readViaInputStream, it hangs with the fill () method in the BufferedInputStream.fill () method with int n = getInIfOpen (). read (buffer, pos, buffer.length - pos); It calls the native method of the FileInputStream class.

The same goes for readViaBufferedReader.

  • Use a different stream to read the output stream.

It also does not work, the output will be:

// Before calling the child process

// After calling the child process

// Read stream completed.

// ===> and the main program freezes

Thanks so much for any answer :)

Code below: Updated to use the Guillaume Polet code provided in the following comment.

public class MainApp { public static enum APP { B, C; } public static void main(String[] args) throws Exception, InterruptedException { if (args.length > 0) { APP app = APP.valueOf(args[0]); switch (app) { case B: performB(); break; case C: performC(); break; } return; } performA(); } private static void performA() throws Exception { String javaBin = "java"; String[] cmdArray = { javaBin, "-cp", System.getProperty("java.class.path"), MainApp.class.getName(), APP.B.name() }; ProcessBuilder builder = new ProcessBuilder(cmdArray); builder.redirectErrorStream(true); final Process process = builder.start(); process.getOutputStream().close(); process.getErrorStream().close(); // if we call this, the main app hangs, the output would be: // Before call child process // After call child process // the main program hangs here. // Never output - "Read stream finished." readViaInputStream(process); // if we call this, the main app hangs, the output would be: // Before call child process // After call child process // the main program hangs here. // Never output - "Read stream finished." // readViaBufferedReader(process); // if we call this, the main app still hangs, the output would be: // Before call child process // After call child process // Read stream finished. // ===> and the main program hang // readOutputViaAnotherThread(process); System.err.println("Read stream finished."); // never come here } private static void performB() throws Exception { System.out.println("Before call child process"); String javaBin = "java"; String[] cmdArray = { javaBin, "-cp", System.getProperty("java.class.path"), MainApp.class.getName(), APP.C.name() }; ProcessBuilder builder = new ProcessBuilder(cmdArray); Process process = builder.start(); process.getInputStream().close(); process.getOutputStream().close(); process.getErrorStream().close(); System.out.println("After call child process"); System.exit(0); // no difference with or without this line. } private static void performC() throws Exception { Thread thread = new Thread() { @Override public void run() { int i = 0; while (true) { try { Thread.sleep(60 * 2); } catch (InterruptedException e) { e.printStackTrace(); } System.err.println("child " + ++i); } } }; thread.start(); thread.join(); } private static void readViaInputStream(final Process process) throws Exception { // System.err.println("exitValue: " + process.waitFor()); InputStream is = process.getInputStream(); int result; while ((result = is.read()) != -1) { System.err.println(result); } } private static void readViaBufferedReader(final Process process) throws Exception { BufferedReader in = new BufferedReader(new InputStreamReader( process.getInputStream(), "utf-8")); String result = ""; while ((result = in.readLine()) != null) { System.err.println(result); } } private static void readOutputViaAnotherThread(final Process process) throws Exception { class ReadOutputStreamThread extends Thread { public void run() { running = true; try { BufferedReader in = new BufferedReader( new InputStreamReader(process.getInputStream(), "utf-8")); String result = ""; while (running && (result = in.readLine()) != null) { System.err.println(result); } } catch (Exception e) { e.printStackTrace(); } }; private volatile boolean running; public void shutdown() throws IOException { running = false; // this has no impact process.getInputStream().close(); interrupt(); } } ReadOutputStreamThread readOutputThread = new ReadOutputStreamThread(); // if we set this readOutputThread as daemon, it works, but the thread // will remains run forever. // readOutputThread.setDaemon(true); readOutputThread.start(); System.err.println("exitValue: " + process.waitFor()); readOutputThread.shutdown(); } 

}

+4
source share
2 answers

When your program freezes in BufferedReader.readLine() , this is most likely due to the fact that the thread does not end with a line terminator.

There are two ways: readLine() continue and return:

  • there is an EOL character, and readLine() returns the string before this character
  • the stream closes and readLine() returns null

In your case, neither of these two possibilities is true, and there is no way for BufferedReader.readLine() to decide whether there will be more characters in the stream with the EOL character later or to the end. Therefore, he blocks and looks forward to what will happen. He expects more characters to come into it, and he will return only when the next EOL char arrives, or the main thread is closed.

Try one of the following:

  • don't use readLine() , but a byte-wise reading method
  • make sure the process returns a result ending with an EOL

Also keep in mind that you close all std streams yourself, even if they are not used.

+4
source

EDIT: Ok found a solution: You need to move the reading of the input stream to another stream and use process.waitFor() to find out when B exits. It seems like you will never get EOF from an inpustream subprocess, and so reading the input stream never ends.

Sorry, this is not an answer, only SSCCE which can be copied and started without any further changes and which reproduces your problem:

 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; public class Test5 { public static enum APP { B, C; } public static void main(String[] args) throws IOException, InterruptedException { if (args.length > 0) { APP app = APP.valueOf(args[0]); switch (app) { case B: performB(); break; case C: performC(); break; } return; } performA(); } private static void performC() throws InterruptedException { Thread thread = new Thread() { @Override public void run() { int i = 0; while (true) { try { Thread.sleep(60 * 2); } catch (InterruptedException e) { e.printStackTrace(); } System.err.println("child " + ++i); } } }; thread.start(); thread.join(); } private static void performB() throws IOException { System.out.println("Before call child process"); String javaBin = "java"; String[] cmdArray = { javaBin, "-cp", System.getProperty("java.class.path"), Test5.class.getName(), APP.C.name() }; ProcessBuilder builder = new ProcessBuilder(cmdArray); // Process process = builder.start(); System.out.println("After call child process"); System.exit(0); // no differnce with or without this line. } private static void performA() throws IOException, UnsupportedEncodingException { String javaBin = "java"; String[] cmdArray = { javaBin, "-cp", System.getProperty("java.class.path"), Test5.class.getName(), APP.B.name() }; ProcessBuilder builder = new ProcessBuilder(cmdArray); builder.redirectErrorStream(true); Process process = builder.start(); BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream(), "utf-8")); String result = ""; while ((result = in.readLine()) != null) { System.err.println(result); } System.err.println("Read stream finished."); // never come here } } 
0
source

Source: https://habr.com/ru/post/1396816/


All Articles