2013-12-06 4 views
1

, поэтому я запускаю процесс на Java, который запускает программу на C++. Программа C++ записывает некоторые данные в OutputStream и изнутри я читаю этот поток.Темы и вопросы по Java: мнение Nedd о реализации

по выполнению выглядит следующим образом:

.... 
    ProcessBuilder pb = new ProcessBuilder(filepath); 
    Process process; 
    InputStream processStream; 
    try { 
     Process p = pb.start(); 
     ProcessOutput processOutput = new ProcessOutput(p.getInputStream()); 
     processOutput.run(); 
     return processOutput.getRequestString(); 
    } catch (IOException e1) { 
     LOG.error(e1.getMessage()); 
    } 

filepath переменная является строка, который указывает на программу исполняемый файл C++. Класс ProcessOutput - это Thread, который считывает InputStream и преобразует его в String, который позже может быть получен через ProcessOutput.getRequestString().

Приложение, похоже, ведет себя правильно, но теперь мне любопытно, могут ли возникнуть какие-либо проблемы с синхронизацией с тем, как я построил решение. Возможно ли, когда я позвоню processOutput.getRequestString(), что процесс еще не завершен или я блокирую выполнение до завершения процесса?

Я занимаюсь разработкой Java в течение некоторого времени, но процессы/потоки все еще запутывают меня. Если кто-то увидит некоторые проблемы с этой реализацией, я был бы рад улучшить его.

В соответствии с просьбой класса ProcessOutput:

import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.InputStreamReader; 

public class ProcessOutput extends Thread { 

    private InputStream inputStream; 
    private String requestString; 

    public ProcessOutput(InputStream inputStream) { 
     this.inputStream = inputStream; 
     setDaemon(true); 
    } 

    public void run() { 
     BufferedReader br = null; 
     StringBuilder sb = new StringBuilder(); 

     String line; 
     try { 

      br = new BufferedReader(new InputStreamReader(this.inputStream)); 
      while ((line = br.readLine()) != null) { 
       sb.append(line); 
      } 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } finally { 
      if (br != null) { 
       try { 
        br.close(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 
     requestString = sb.toString(); 
    } 

    public String getRequestString() { 
     return requestString; 
    } 

} 

Edit: Как zapl упомянутых в комментариях я не набирает ничего от использования Thread с применением зависит от результата процесса и должен дождитесь его окончания в любом случае.

+0

Почему бы не использовать JNI для вызова кода на C++ вместо прохождения процесса и т. Д.? – Krease

+1

Я думаю, что ответ находится в методе run() вашего класса ProcessOutput. Прочитывает ли он до конца потока или возвращается раньше? – isnot2bad

+0

@ Chris Почему, по-вашему, JNI лучше подходит в этом случае? – isnot2bad

ответ

1

Вы можете использовать некоторые синхронизаторы, такие как обратный отсчет или циклический барьер и т. Д. Я добавил реализацию обратного отсчета, как показано ниже. Я не вижу вреда в управлении логикой ввода-вывода в отдельном потоке, если вы думаете, что в некоторых случаях это может произойти. В вызове getRequestString вы можете решить, как вы хотите рассматривать сценарий, если он занимает слишком много времени.

public class ProcessOutput { 
    private final InputStream inputStream; 
    final CountDownLatch latch = new CountDownLatch(1); 

    public ProcessOutput(InputStream inputStream) { 
     this.inputStream = inputStream; 
    } 

    public String getRequestString(final long timeoutMilliSeconds) throws InterruptedException { 
     StreamReader reader = new StreamReader(); 
     new Thread(reader).start(); 
     latch.await(timeoutMilliSeconds,TimeUnit.MILLISECONDS); 
     //You can check if requestString not yet set or null after waiting for timeout throw some application exception to tell the client that its taking too long. 
     return reader.getRequestString(); 
    } 

    private class StreamReader implements Runnable { 

     String requestString; 
     @Override 
     public void run() { 
      BufferedReader br = null; 
      StringBuilder sb = new StringBuilder(); 

      String line; 
      try { 

       br = new BufferedReader(new InputStreamReader(inputStream)); 
       while ((line = br.readLine()) != null) { 
        sb.append(line); 
       } 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } finally { 
       if (br != null) { 
        try { 
         br.close(); 
        } catch (IOException e) { 
         e.printStackTrace(); 
        } 
       } 
      } 
      requestString = sb.toString(); 
      //Work done now 
      latch.countDown(); 
     } 

     public String getRequestString() { 
      return requestString; 
     } ; 
    } 
} 
1

Возможно, вы могли бы добавить join() в processOutput Thread, чтобы он дождался завершения процесса ProcessBuilder Thread и выполнил требуемый запрос String.

2

Вы можете вызвать process.waitFor() для кода, ожидающего завершения процесса. Но, как предложение, это не очень хорошая практика для непосредственного вызова процесса, если вы не потребляете потоки ошибок/выходных данных. Вы можете использовать библиотеку apache commons exec, которая удобна.

Смежные вопросы