Javaアプリケーションは3つあり、appBはappCを起動して永遠に実行します。 appAはappBを開始し、その出力を読み取ります。 しかし、appAは決して終了しません。誰もが、なぜこれが起こり、どのように解決するのか考えています。 appAでストリームを読み取らないとハングしません。プロセスBの出力ストリームを読み込んで別のプロセスCを起動するとハングします永久に実行する
私は2つの方法で試してみました:直接
- 読む出力ストリーム(readViaInputStream)またはBufferedReaderの(readViaBufferedReaderを)。
彼らは動作しません、出力は次のようになります。
//私たちは、メインのアプリがハング、それらを呼び出す場合、出力は次のようになります。コールの子プロセスの前に
//
//コール後の子プロセス
//メインプログラムがハングします。
//出力しない - 「読み込みストリームが終了しました。」
readViaInputStreamでは、int n = getInIfOpen()でBufferedInputStream.fill()メソッドのfill()メソッドでハングします。read(buffer、pos、buffer.length - pos); FileInputStreamクラスのネイティブメソッドを呼び出します。
ViaBufferedReaderメソッドと同じです。
- 別のスレッドを使用して出力ストリームを読み取ります。 それはあまりにも動作しない
、出力は次のようになります。コールの子プロセスの前に
//
//コールの子プロセスの後
//読むストリームが終了しました。
// ===>とメインプログラムは、任意の返信用
おかげで非常に多くのハング:)
コードは以下の通りである - ギョームのPoletは、次のコメントで提供されたコードを使用するように更新します。
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();
}
}
javadocのthread.joinを読んだことがありますか? – Scorpion
はい、子プロセスのメインスレッドは永遠に待っているようです。生成されたスレッドはデーモンではないため、join呼び出しは不要です。質問によれば、問題はappCではなく、入力ストリームから永遠に読み取るappAにあります。これは、ストリームの他の部分がストリームを閉じないために発生します。 –
返信いただきありがとうございます。 私が提供したコードは実際のプロジェクトのものです。プロセスCは永遠に実行されることを意図しています。 - 唯一の違いは、私の実際のプロジェクトでは、C#アプリケーションがアプリケーションBを起動し、その出力と同じ問題を読むことです。 jdkソースコードをデバッグした後、BufferedReaderのfill()メソッドを呼び出し、最後にsun.nio.cs.StreamDecoder.read()メソッドを呼び出すBufferedReader.readLine()でハングアップするようです。 プライベートvoid fill()throws IOException { do {n = in.read(cb、dst、cb.length - dst); //これがここにかかります } while(n == 0);} これはJDKのバグかどうかわかりません。 –