gpt4 book ai didi

java - 主程序在读取启动另一个永远运行的进程的进程的输出流时挂起

转载 作者:搜寻专家 更新时间:2023-11-01 02:31:01 25 4
gpt4 key购买 nike

有三个 Java 应用程序,appB 只启动 appC,它会一直运行。 appA 启动 appB 并读取其输出。但是 appA 永远不会退出。任何人都知道为什么会发生这种情况以及如何解决它。如果我不读取 appA 中的流,它不会挂起。

我试过两种方法:

  1. 直接读取输出流(readViaInputStream)或BufferedReader(readViaBufferedReader)。

它们不起作用,输出将是:

//如果我们调用它们,主应用挂起,输出将是:

//调用子进程前

//调用后子进程

//主程序卡在这里

//从不输出 - “读取流完成。”

在 readViaInputStream 中,它卡在 BufferedInputStream.fill() 方法中的 fill() 方法处 at int n = getInIfOpen().read(buffer, pos, buffer.length - pos);它调用 FileInputStream 类的本地方法。

与 readViaBufferedReader 方法相同。

  1. 使用另一个线程读取输出流。

它也不起作用,输出将是:

//调用子进程之前

//调用后子进程

//读流完成。

//===> 并且主程序挂了

非常感谢任何回复:)

代码如下 - 更新为使用下一条评论中提供的代码 Guillaume 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();
}

最佳答案

当您的程序在 BufferedReader.readLine() 中挂起时,这很可能是因为流没有以行尾字符结尾。

有两种方式让readLine()继续并返回:

  • 有一个 EOL 字符,readLine() 返回该字符之前的行
  • 流被关闭并且 readLine() 返回 null

在您的情况下,这两种可能性都不是真的,并且 BufferedReader.readLine() 无法确定以后是否会在流中出现更多带有 EOL 字符的字符,或者结束了。所以它阻止并期待将要发生的事情。它期待更多的字符进入,并且仅当下一个 EOL-char 进入或底层流关闭时它才会返回。

尝试以下操作之一:

  • 不使用readLine(),而是使用字节读取方法
  • 确保进程将产生以 EOL 字符结尾的输出

另请注意,您必须 close all std-Streams自己 - 即使不使用。

关于java - 主程序在读取启动另一个永远运行的进程的进程的输出流时挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9310077/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com