There are three Java applications, appB just starts appC which would run forever. appA starts appB and read its output. But appA never exit. Anyone has idea why this happen and how to resolve it. It doesn't hang if I don't read the stream in appA.
I have tried two ways:
- Read output stream(readViaInputStream) or BufferedReader(readViaBufferedReader) directly.
They doesn't work, the output would be:
// if we call them, 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."
In readViaInputStream, it hangs at fill() method in BufferedInputStream.fill() method at int n = getInIfOpen().read(buffer, pos, buffer.length - pos); It calls the native method of FileInputStream class.
Same to readViaBufferedReader method.
- Use another thread to read output stream.
It doesn't work too, the output would be:
// Before call child process
// After call child process
// Read stream finished.
// ===> and the main program hang
Thanks very much for any reply : )
The code is as below - updated to use code Guillaume Polet provided in next 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();
}
}