同时使用BufferedReaders挂起读取输入和错误流

时间:2009-08-28 20:53:27

标签: java input runtime

首先让我向SO社区道歉,告诉你一些应该是如此微不足道的东西。但是我整天都在这里,而且我已经走到了尽头。

我的程序中有一部分需要从输入流中提取文本,并从使用Runtime.getrunTime()。exec()启动的进程中获取错误流,并将其有序地传递给标准输入和输出方式。我有一个功能,因为我可以告诉应该工作。但它似乎陷入了陷阱 - 它正在等待流报告准备好 - 但流已经完成并且没有报告。我很困惑。我想不出另一种方法可以做到这符合我的约束,而我更加怀疑这样的捕获22可以存在。

这是我的代码:

private void forwardStreamtoStd(InputStream in, InputStream err) 
throws IOException {
    int c = -1;
    BufferedReader inReader = new BufferedReader(
        new InputStreamReader(in, "US-ASCII"));
    BufferedReader errReader = new BufferedReader(
        new InputStreamReader(err, "US-ASCII"));
    boolean inFinished = false, errFinished = false;

    try {
        System.out.println("Begin stream read loop...");
        while (!inFinished && !errFinished) {
        if (!inFinished) {
            while (inReader.ready()) {
                if ((c = inReader.read()) == -1) {
                    inFinished = true;
                } 
                else {
                    System.out.print((char) c);
                }
            }
        }

        if (!errFinished) {
            while (errReader.ready()) {
                if ((c = errReader.read()) == -1) {
                    errFinished = true;
                } 
                else {
                     System.err.print((char) c);
                }
            }
        }
        }
        System.out.println("End stream read loop.");
    } 
    catch (IOException e) {
        throw e;
    } 
    finally {
        errReader.close();
        inReader.close();
    }
}

问题似乎是读取循环正在等待流报告准备好,结果是没有看到读取返回的-1告诉他们是时候退出了。我试图避免流阻塞,这样我就可以在他们准备好时依次从两者中取出。但是,如何才能捕获进程的流结束?我错过了什么吗?不应该读报告它在流-1结束时被读取?这些过程正在完成,所以他们的流应该死了。我在这里做错了什么?

4 个答案:

答案 0 :(得分:8)

还有两种可能性:

  • 使用ProcessBuilder并调用redirectErrorStream(true)加入两个流,您需要读取一个流。我有一个例子here
  • 在JDK7中,您可以调用inheritIO()自动转发所有内容

编辑在第二个猜测中,似乎ready()调用误导了您的程序。试试这个:

private void forwardStreamtoStd(InputStream in, InputStream err) 
throws IOException {
    int c = -1;
    BufferedReader inReader = new BufferedReader(
        new InputStreamReader(in, "US-ASCII"));
    BufferedReader errReader = new BufferedReader(
        new InputStreamReader(err, "US-ASCII"));
    boolean inFinished = false, errFinished = false;

    try {
        System.out.println("Begin stream read loop...");
        if (!inFinished) {
            while ((c = inReader.read()) != -1) {
                    System.out.print((char) c);
            }
            inFinished = true;
        }

        if (!errFinished) {
            while ((c = errReader.read()) != -1) {
                System.err.print((char) c);
            }
            errFinished = true;
        }
        System.out.println("End stream read loop.");
    } 
    catch (IOException e) {
        throw e;
    } 
    finally {
        errReader.close();
        inReader.close();
    }
}

或者更好的是,如果你没有计划任何额外的转换,请不要使用BufferedReader:

private void createReader(final InputStream in, final OutputStream out) {
    new Thread() {
        public void run() {
            try {
                int c = 0;
                while ((c = in.read()) != -1) {
                    out.write(c);
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            } finally {
                in.close();
            }
        }
    }.start();
}
private void forwardStreamtoStd(InputStream in, InputStream err) 
throws IOException {
    createReader(in, System.out);
    createReader(err, System.err);
}

答案 1 :(得分:5)

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4090471

我一直使用的解决方案是创建一个单独的线程来读取其中一个流,在主线程完成读取时加入线程,然后等待该过程。

答案 2 :(得分:3)

必需同时使用2个流,以防止阻塞。有关详细信息,请参阅this article,特别注意在单独的线程中捕获stdout / err的StreamGobbler机制。

答案 3 :(得分:1)

如果我没记错的话,生成的进程永远不会关闭流 - 所以你需要让读者进入自己的线程,在主线程上休眠直到进程完成,然后关闭阅读器。