ProcessBuilder и потоки гоблера - PullRequest
1 голос
/ 18 марта 2019

При поиске в Интернете учебников о том, как запускать подпроцесс в Java и обрабатывать stdin, stdout и stderr, я нахожу решения только с потоками гоблера.Использование потоков гоблера означает создание, планирование и очистку 3 потоков для каждого вызова подпроцесса.Если называются только несколько процессов, эти дополнительные издержки не имеют значения, но если вызываются тысячи подпроцессов, например, компиляция большого количества файлов, эти издержки создают измеримое более длительное время обработки.Кроме того, использование потоков гоблера делает реализацию более сложной.

Так что мой вопрос: почему часто используют такое неэффективное и сложное решение?

Ниже приведено гораздо более простое и более эффективное решение:

private void runProcess() throws IOException, InterruptedException {
  this.prc = new ProcessBuilder(this.commandLine.split(" ")).start();

  Iterator<String> it = this.getInputstreamList().iterator();
  StringBuilder stdOut = new StringBuilder();
  StringBuilder stdErr = new StringBuilder();

  try (BufferedWriter stdInBw = new BufferedWriter(new OutputStreamWriter(this.prc.getOutputStream()), 65536)) {
    while (it.hasNext()) {
      String line = it.next();
      stdInBw.write(line + "\n");
    }
    stdInBw.flush();
  }
  try (BufferedReader stdOutBr = new BufferedReader(new InputStreamReader(this.prc.getInputStream()), 65536)) {
    try (BufferedReader stdErrBr = new BufferedReader(new InputStreamReader(this.prc.getErrorStream()), 65536)) {
      while (true) {
        while (stdOutBr.ready()) {
          String line = stdOutBr.readLine();

          if (line == null) {
            break;
          }
          stdOut.append(line + "\n");
        }
        while (stdErrBr.ready()) {
          String line = stdErrBr.readLine();

          if (line == null) {
            break;
          }
          stdErr.append(line + "\n");
        }
        if (!this.prc.isAlive()) {
          break;
        }
        this.prc.waitFor(50, TimeUnit.MILLISECONDS);
      }
    }
  }
  this.retVal = prc.exitValue();
  this.stdOut = stdOut.toString();
  this.stdErr = stdErr.toString();
}

Мои тесты показали, что вышеуказанное решение работает как шарм.Но, возможно, я что-то упустил, что делает вышеприведенное решение непригодным в особых случаях.Есть намеки или сомнения?

...