2017-03-13 26 views
6

Javaから外部プロセスのストリーム(IO)を生成して消費する適切な方法は何ですか?私が知る限り、Javaの入力ストリーム(プロセス出力)は、おそらく制限されたバッファサイズのためにプロセス入力を生成するのと並行してスレッドで消費されるべきです。Java execメソッド、ストリームを正しく処理する方法

しかし最終的にそれらのコンシューマスレッドと同期する必要があるかどうかわからない、またはすべてのプロセス出力が実際に消費されていることを確認するために、プロセスがwaitForメソッドで終了するのを待つだけで十分ですか? I.Eは可能です。プロセスが終了しても(出力ストリームを閉じます)、まだストリームのJava側に未読データがありますか? waitForは、プロセスがいつ完了したかを実際にどのように知っていますか?問題のプロセスについては、EOF(入力ストリームのJava終了を閉じる)が終了を知らせます。ストリームを処理するために

私の現在のソリューション

public class Application { 

    private static final StringBuffer output = new StringBuffer(); 
    private static final StringBuffer errOutput = new StringBuffer(); 
    private static final CountDownLatch latch = new CountDownLatch(2); 

    public static void main(String[] args) throws IOException, InterruptedException { 

     Process exec = Runtime.getRuntime().exec("/bin/cat"); 

     OutputStream procIn = exec.getOutputStream(); 
     InputStream procOut = exec.getInputStream(); 
     InputStream procErrOut = exec.getErrorStream(); 

     new Thread(new StreamConsumer(procOut, output)).start(); 
     new Thread(new StreamConsumer(procErrOut, errOutput)).start(); 

     PrintWriter printWriter = new PrintWriter(procIn); 

     printWriter.print("hello world"); 
     printWriter.flush(); 
     printWriter.close(); 

     int ret = exec.waitFor(); 
     latch.await(); 

     System.out.println(output.toString()); 
     System.out.println(errOutput.toString()); 
    } 

    public static class StreamConsumer implements Runnable { 

     private InputStream input; 
     private StringBuffer output; 

     public StreamConsumer(InputStream input, StringBuffer output) { 
      this.input = input; 
      this.output = output; 
     } 

     @Override 
     public void run() { 
      BufferedReader reader = new BufferedReader(new InputStreamReader(input)); 
      String line; 
      try { 
       while ((line = reader.readLine()) != null) { 
        output.append(line + System.lineSeparator()); 
       } 
      } catch (IOException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } finally { 
       try { 
        reader.close(); 
       } catch (IOException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } finally { 
        latch.countDown(); 
       } 
      } 
     } 

    } 
} 

次され、それはここで、ラッチを使用することが必要である、またはwaitForは、すべての出力が既に消費され巻き込むのか?また、出力が終了しない/新しい行が含まれている場合は、readLineは出力を見逃しているのでしょうか? nullを読み込むプロセスがストリームの終わりを閉じているのですか?ヌルを読み取ることができる他のシナリオはありますか?

ストリームを処理する正しい方法は何ですか、私の例よりも何か良いことができますか?

答えて

1

waitForは、プロセスが終了したことを通知しますが、stdoutとstderrから文字列を収集するスレッドも終了していることを確認できません。したがって、ラッチを使用することは正しい方向のステップです。 代わりにラッチを待って、あなたは直接のスレッドを待つことができます。

Thread stdoutThread = new Thread(new StreamConsumer(procOut, output)).start(); 
Thread stderrThread = ... 
... 
int ret = exec.waitFor(); 
stdoutThread.join(); 
stderrThread.join(); 

ところで、StringBuffer秒で行を格納することは役に立たない仕事です。代わりにArrayList<String>を使用し、変換を行わずにそこに行を置き、最後にループでそれらを取得します。

1

あなたのappapproachは正しいですが、CountDownLatchを削除してThreadPoolを使用し、新しいThreadを直接作成しない方が良いです。 ThreadPoolから2つの未来が得られます。完了まで待つことができます。

しかし、私は最終的にこれらの消費者スレッドと同期する必要がある、またはそれだけですべてのプロセス出力が実際に消費されていることを確信するために、WaitForメソッドで終了するプロセスを待つために十分であるかどうかわからないんだけど? I.Eは可能です。プロセスが終了しても(出力ストリームを閉じます)、まだストリームのJava側に未読データがありますか?

はい、この状況が発生する可能性があります。 IOストリームの終了と読み取りは無関係のプロセスです。

+0

ありがとうございます。残念ながら、コードはJDK6に準拠している必要があります –

+0

JavaPoolからThreadPoolが存在します。 [Executors](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html)を参照してください。 – kurt

関連する問題