Перейти к основному содержимому

Руководство по API java.lang.Process

· 6 мин. чтения

1. Введение

В этом руководстве мы подробно рассмотрим Process API .

Для более подробного изучения того, как использовать Process для выполнения команды оболочки, мы можем обратиться к нашему предыдущему руководству здесь .

Процесс, на который он ссылается, является выполняющимся приложением. Класс Process предоставляет методы для взаимодействия с этими процессами, включая извлечение вывода, выполнение ввода, мониторинг жизненного цикла, проверку состояния выхода и его уничтожение (уничтожение).

2. Использование класса процесса для компиляции и запуска Java-программы

Давайте посмотрим на пример компиляции и запуска другой Java-программы с помощью Process API:

@Test
public void whenExecutedFromAnotherProgram_thenSourceProgramOutput3() throws IOException {

Process process = Runtime.getRuntime()
.exec("javac -cp src src\\main\\java\\com\\foreach\\java9\\process\\OutputStreamExample.java");
process = Runtime.getRuntime()
.exec("java -cp src/main/java com.foreach.java9.process.OutputStreamExample");
BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()));
int value = Integer.parseInt(output.readLine());

assertEquals(3, value);
}

Таким образом, возможности выполнения Java-кода в существующем Java-коде практически безграничны.

3. Создание процесса

Наше Java-приложение может вызывать любое приложение, работающее в нашей компьютерной системе, с учетом ограничений операционной системы.

Поэтому мы можем выполнять приложения. Давайте посмотрим, какие различные варианты использования мы можем запустить, используя Process API.

Класс ProcessBuilder позволяет нам создавать подпроцессы в нашем приложении.

Давайте посмотрим демонстрацию открытия приложения «Блокнот» на базе Windows:

ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();

4. Уничтожение процесса

Процесс также предоставляет нам методы для уничтожения подпроцессов или процесса. Хотя способ уничтожения приложения зависит от платформы .

Давайте посмотрим на различные варианты использования, которые возможны.

4.1. Уничтожение процесса по ссылке

Допустим, мы используем ОС Windows и хотим создать приложение «Блокнот» и уничтожить его.

Как и прежде, мы можем создать экземпляр приложения «Блокнот», используя класс ProcessBuilder и метод start() .

Затем мы можем вызвать метод destroy() для нашего объекта Process .

4.2. Уничтожение процесса по идентификатору

Мы также можем убивать процессы, запущенные в нашей операционной системе, которые могут быть созданы не нашим приложением.

При этом следует соблюдать осторожность, так как мы можем неосознанно уничтожить критический процесс, который может сделать операционную систему нестабильной .

Сначала нам нужно узнать идентификатор текущего запущенного процесса, проверив диспетчер задач, и узнать pid.

Давайте посмотрим пример:

long pid = /* PID to kill */;
Optional<ProcessHandle> optionalProcessHandle = ProcessHandle.of(pid);
optionalProcessHandle.ifPresent(processHandle -> processHandle.destroy());

4.3. Уничтожение процесса принудительно

При выполнении метода destroy() подпроцесс будет уничтожен, как мы видели ранее в статье.

В случае, когда destroy() не работает, у нас есть вариант destroyForcably() .

Мы всегда должны начинать с метода destroy() . После этого мы можем выполнить быструю проверку подпроцесса, выполнив isAlive() .

Если он возвращает true, выполните destroyForcably() :

ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
process.destroy();
if (process.isAlive()) {
process.destroyForcibly();
}

5. Ожидание завершения процесса

У нас также есть два перегруженных метода, с помощью которых мы можем гарантировать, что можем дождаться завершения процесса.

5.1. ждать()

Когда этот метод выполняется, он переводит текущий поток процесса выполнения в состояние ожидания блокировки, если только подпроцесс не будет завершен .

Давайте посмотрим на пример:

ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
assertThat(process.waitFor() >= 0);

Из приведенного выше примера видно, что текущий поток для продолжения выполнения будет продолжать ждать завершения потока подпроцесса. После завершения подпроцесса текущий поток продолжит свое выполнение.

5.2. ждать (длительный timeOut, время TimeUnit)

Когда этот метод выполняется, он помещает текущий поток процесса выполнения в состояние ожидания блокировки, если только подпроцесс не будет завершен или не закончится время .

Давайте посмотрим на пример:

ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
assertFalse(process.waitFor(1, TimeUnit.SECONDS));

Из приведенного выше примера видно, что текущий поток для продолжения выполнения будет продолжать ждать завершения потока подпроцесса или истечения указанного интервала времени.

Когда этот метод выполняется, он возвращает логическое значение true, если подпроцесс завершился, или логическое значение false, если время ожидания истекло до выхода подпроцесса.

6. выходное значение ()

Когда этот метод запущен, текущий поток не будет ждать завершения или уничтожения подпроцесса, однако он вызовет исключение IllegalThreadStateException , если подпроцесс не завершен.

И наоборот, если подпроцесс был успешно завершен, это приведет к выходному значению процесса .

Это может быть любое возможное положительное целое число.

Давайте рассмотрим пример, когда метод exitValue() возвращает положительное целое число, когда подпроцесс был успешно завершен:

@Test
public void
givenSubProcess_whenCurrentThreadWillNotWaitIndefinitelyforSubProcessToEnd_thenProcessExitValueReturnsGrt0()
throws IOException {
ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
assertThat(process.exitValue() >= 0);
}

7. жив ()

Когда мы хотим выполнить бизнес-обработку, которая субъективна независимо от того, активен процесс или нет.

Мы можем выполнить быструю проверку, чтобы узнать, жив ли процесс или нет, который возвращает логическое значение.

Давайте посмотрим на быстрый пример:

ProcessBuilder builder = new ProcessBuilder("notepad.exe");
Process process = builder.start();
Thread.sleep(10000);
process.destroy();
assertTrue(process.isAlive());

8. Обработка потоков процессов

По умолчанию созданный подпроцесс не имеет своего терминала или консоли. Все его стандартные операции ввода-вывода (например, stdin, stdout, stderr) будут отправлены родительскому процессу. Таким образом, родительский процесс может использовать эти потоки для подачи входных данных и получения выходных данных от подпроцесса.

Следовательно, это дает нам огромную гибкость, поскольку дает нам контроль над вводом/выводом нашего подпроцесса.

8.1. получитьпоток ошибок()

Интересно, что мы можем получить ошибки, сгенерированные из подпроцесса, и затем выполнить бизнес-обработку.

После этого мы можем выполнить определенные проверки бизнес-процессов в соответствии с нашими требованиями.

Давайте посмотрим пример:

@Test
public void givenSubProcess_whenEncounterError_thenErrorStreamNotNull() throws IOException {
Process process = Runtime.getRuntime().exec(
"javac -cp src src\\main\\java\\com\\foreach\\java9\\process\\ProcessCompilationError.java");
BufferedReader error = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String errorString = error.readLine();
assertNotNull(errorString);
}

8.2. получить входной поток ()

Мы также можем получить выходные данные, сгенерированные подпроцессом, и использовать их в родительском процессе, что позволит обмениваться информацией между процессами:

@Test
public void givenSourceProgram_whenReadingInputStream_thenFirstLineEquals3() throws IOException {
Process process = Runtime.getRuntime().exec(
"javac -cp src src\\main\\java\\com\\foreach\\java9\\process\\OutputStreamExample.java");
process = Runtime.getRuntime()
.exec("java -cp src/main/java com.foreach.java9.process.OutputStreamExample");
BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()));
int value = Integer.parseInt(output.readLine());

assertEquals(3, value);
}

8.3. получить tOutputStream ()

Мы можем отправить входные данные в подпроцесс из родительского процесса:

Writer w = new OutputStreamWriter(process.getOutputStream(), "UTF-8");
w.write("send to child\n");

8.4. Фильтровать потоки процессов

Это вполне допустимый вариант использования для взаимодействия с выборочно запущенными процессами.

Process предоставляет нам возможность выборочно фильтровать запущенные процессы на основе определенного предиката.

После этого мы можем выполнять бизнес-операции над этим выборочным набором процессов:

@Test
public void givenRunningProcesses_whenFilterOnProcessIdRange_thenGetSelectedProcessPid() {
assertThat(((int) ProcessHandle.allProcesses()
.filter(ph -> (ph.pid() > 10000 && ph.pid() < 50000))
.count()) > 0);
}

9. Заключение

Process — это мощный класс для взаимодействия на уровне операционной системы. Запуск команд терминала, а также запуск, мониторинг и уничтожение приложений.

Чтобы узнать больше о Java 9 Process API, ознакомьтесь с нашей статьей здесь .

Как всегда, вы найдете исходники на Github .