4

几天前我在这里发布了同样的问题(Java 使用 inputstream 从外部程序读取标准输出),我发现了一些很好的建议来处理读取时的块(while(is.read()) != -1)),但我仍然无法解决问题。

在阅读了这个类似问题的答案后,

Java InputStream 阻塞读取 (尤其是 Guss 发布的答案),

我开始相信使用 is.read() != -1 条件循环输入流如果程序是交互式的(即它需要用户的多个输入并在后续输入时呈现额外的输出,并且程序仅在给出明确的退出命令时才退出)。我承认我对多线程了解不多,但我认为我需要一种机制来在需要用户输入时迅速暂停输入流线程(每个用于 stdout、stderr),并在输入后恢复提供以防止阻塞。以下是我当前的代码,它在指示的行上遇到了块:

EGMProcess egm = new EGMProcess(new String[]{directory + "/egm", "-o",
                “CasinoA”、“-v”、“VendorA”、“-s”、“localhost:8080/gls/MessageRobot.action”、
                “-E”、“glss_env_cert.pem”、“-S”、“glss_sig_cert.pem”、“-C”、“glsc_sig_cert.pem”、
                “-d”、“config”、“-L”、“config/log.txt”、“-H”、“GLSA-SampleHost”}、新字符串[]{“PATH=${PATH}”}、目录);

    egm.execute();

    BufferedReader stdout = new BufferedReader(new InputStreamReader(egm.getInputStream()));
    BufferedReader stderr = new BufferedReader(new InputStreamReader(egm.getErrorStream()));

    EGMStreamGobbler stdoutprocessor = new EGMStreamGobbler(stdout, egm);
    EGMStreamGobbler stderrprocessor = new EGMStreamGobbler(stderr, egm);

    BufferedWriter stdin = new BufferedWriter(new OutputStreamWriter(egm.getOutputStream()));


    stderrprocessor.run(); //<-- the block occurs here!
    stdoutprocessor.run();


    //EGM/Agent test cases


        //check bootstrap menu
        if(!checkSimpleResult("******** EGM Bootstrap Menu **********", egm))
        {
            String stdoutdump = egm.getStdOut();
            egm.cleanup();
            throw new Exception("can't find '******** EGM Bootstrap Menu **********'" +
                "in the stdout" + "\nStandard Output Dump:\n" + stdoutdump);
        }

        //select bootstrap
        stdin.write("1".toCharArray());
        stdin.flush();

        if(!checkSimpleResult("Enter port to receive msgs pushed from server ('0' for no push support)", egm)){
            String stdoutdump = egm.getStdOut();
            egm.cleanup();
            throw new Exception("can't find 'Enter port to receive msgs pushed from server ('0' for no push support)'" +
                    "in the stdout" + "\nStandard Output Dump:\n" + stdoutdump);
        }

...

public class EGMStreamGobbler implements Runnable{

private BufferedReader instream; private EGMProcess egm; public EGMStreamGobbler(BufferedReader isr, EGMProcess aEGM) { instream = isr; egm = aEGM; } public void run() { try{ int c; while((c = instream.read()) != 1) { egm.processStdOutStream((char)c); } } catch(IOException e) { e.printStackTrace(); } }

}

我为代码的长度道歉,但我的问题是,

1) 有没有办法在不使用 read() 的情况下控制接收输入流(stdout、stderr)的过程?还是我只是执行得很糟糕?

2) 多线程是开发接收输入流和编写输出过程的正确策略吗?

PS:如果有人能提供类似问题的解决方案,对我有很大帮助!

4

4 回答 4

8

代替

stderrprocessor.run(); //<-- the block occurs here!
stdoutprocessor.run();

您需要启动线程:

Thread errThread = new Thread(stderrprocessor);
errThread.setDaemon( true );
errThread.start();

Thread outThread = new Thread(stdoutprocessor);
outThread.setDaemon( true );
outThread.start();

run()只是中指定的一种方法RunnableThread.start()在新run()的.RunnableThread

于 2009-07-09T18:13:47.347 回答
2
  1. 如果您只是在可运行对象上调用 #run(),它将不会并行执行。要并行运行它,您必须生成一个 java.lang.Thread,它执行 Runnable 的#run()。
  2. 流是否阻塞取决于流的两侧。如果发送方未发送任何数据或接收方未收到数据,则您处于阻塞状态。如果处理器必须做某事,而流被阻塞,您需要在处理器中生成一个(另一个)线程来等待新数据并在新数据流式传输时中断备用进程。
于 2009-07-09T18:17:49.273 回答
1

首先,您需要阅读 Thread 和 Runnable。您不直接调用 Runnable.run() ,而是设置线程来执行此操作,然后启动线程。

但更重要的是,三个独立线程的存在意味着需要一些仔细的设计。为什么是3线程?你刚开始的两个,和主要的。

我假设您的应用程序的一般想法是等待一些输出到达,解释它并因此向您正在控制的应用程序发送命令?

所以你的主线程需要等待一个读者线程说“啊哈!这很有趣,最好问问用户他想做什么。”

换句话说,您需要一些读者和作者之间的沟通机制。这可以使用 Java 的事件机制来实现。恐怕还要多读书。

于 2009-07-09T18:21:30.920 回答
1

这不就是 nio 创建的原因吗?

我对 nio 中的 Channels 了解不多,但这个答案可能会有所帮助。它展示了如何使用 nio 读取文件。可能有用。

于 2009-07-09T18:24:40.197 回答