Java process waitfor - IT Справочник
Llscompany.ru

IT Справочник
0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Java process waitfor

Многопоточное программирование в Java 8. Часть первая. Параллельное выполнение кода с помощью потоков

    Переводы, 8 июля 2015 в 16:58

Добро пожаловать в первую часть руководства по параллельному программированию в Java 8. В этой части мы на простых примерах рассмотрим, как выполнять код параллельно с помощью потоков, задач и сервисов исполнителей.

Впервые Concurrency API был представлен вместе с выходом Java 5 и с тех пор постоянно развивался с каждой новой версией Java. Большую часть примеров можно реализовать на более старых версиях, однако в этой статье я собираюсь использовать лямбда-выражения. Если вы все еще не знакомы с нововведениями Java 8, рекомендую посмотреть мое руководство.

Потоки и задачи

Все современные операционные системы поддерживают параллельное выполнение кода с помощью процессов и потоков. Процесс — это экземпляр программы, который запускается независимо от остальных. Например, когда вы запускаете программу на Java, ОС создает новый процесс, который работает параллельно другим. Внутри процессов мы можем использовать потоки, тем самым выжав из процессора максимум возможностей.

Потоки (threads) в Java поддерживаются начиная с JDK 1.0. Прежде чем запустить поток, ему надо предоставить участок кода, который обычно называется «задачей» (task). Это делается через реализацию интерфейса Runnable , у которого есть только один метод без аргументов, возвращающий void — run() . Вот пример того, как это работает:

Поскольку интерфейс Runnable функциональный, мы можем использовать лямбда-выражения, которые появились в Java 8. В примере мы создаем задачу, которая выводит имя текущего потока на консоль, и запускаем ее сначала в главном потоке, а затем — в отдельном.

Результат выполнения этого кода может выглядеть так:

Из-за параллельного выполнения мы не можем сказать, будет наш поток запущен до или после вывода «Done!» на экран. Эта особенность делает параллельное программирование сложной задачей в больших приложениях.

Sportmaster Lab, Москва

Потоки могут быть приостановлены на некоторое время. Это весьма полезно, если мы хотим сэмулировать долго выполняющуюся задачу. Например, так:

Когда вы запустите этот код, вы увидите секундную задержку между выводом первой и второй строки на экран. TimeUnit — полезный класс для работы с единицами времени, но то же самое можно сделать с помощью Thread.sleep(1000) .

Работать с потоками напрямую неудобно и чревато ошибками. Поэтому в 2004 году в Java 5 добавили Concurrency API. Он находится в пакете java.util.concurrent и содержит большое количество полезных классов и методов для многопоточного программирования. С тех пор Concurrency API непрерывно развивался и развивается.

Давайте теперь подробнее рассмотрим одну из самых важных частей Concurrency API — сервис исполнителей (executor services).

Исполнители

Concurrency API вводит понятие сервиса-исполнителя (ExecutorService) — высокоуровневую замену работе с потоками напрямую. Исполнители выполняют задачи асинхронно и обычно используют пул потоков, так что нам не надо создавать их вручную. Все потоки из пула будут использованы повторно после выполнения задачи, а значит, мы можем создать в приложении столько задач, сколько хотим, используя один исполнитель.

Вот как будет выглядеть наш первый пример с использованием исполнителя:

Класс Executors предоставляет удобные методы-фабрики для создания различных сервисов исполнителей. В данном случае мы использовали исполнитель с одним потоком.

Результат выглядит так же, как в прошлый раз. Но у этого кода есть важное отличие — он никогда не остановится. Работу исполнителей надо завершать явно. Для этого в интерфейсе ExecutorService есть два метода: shutdown() , который ждет завершения запущенных задач, и shutdownNow() , который останавливает исполнитель немедленно.

Вот как я предпочитаю останавливать исполнителей:

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

Callable и Future

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

Давайте напишем задачу, которая возвращает целое число после секундной паузы:

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

После отправки задачи исполнителю мы сначала проверяем, завершено ли ее выполнение, с помощью метода isDone() . Поскольку задача имеет задержку в одну секунду, прежде чем вернуть число, я более чем уверен, что она еще не завершена.

Вызов метода get() блокирует поток и ждет завершения задачи, а затем возвращает результат ее выполнения. Теперь future.isDone() вернет true , и мы увидим на консоли следующее:

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

Вы, возможно, заметили, что на этот раз мы создаем сервис немного по-другому: с помощью метода newFixedThreadPool(1) , который вернет исполнителя с пулом в один поток. Это эквивалентно вызову метода newSingleThreadExecutor() , однако мы можем изменить количество потоков в пуле.

Таймауты

Любой вызов метода future.get() блокирует поток до тех пор, пока задача не будет завершена. В наихудшем случае выполнение задачи не завершится никогда, блокируя ваше приложение. Избежать этого можно, передав таймаут:

Выполнение этого кода вызовет TimeoutException :

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

InvokeAll

Исполнители могут принимать список задач на выполнение с помощью метода invokeAll() , который принимает коллекцию callable-задач и возвращает список из Future .

В этом примере мы использовали функциональные потоки Java 8 для обработки задач, возвращенных методом invokeAll . Мы прошлись по всем задачам и вывели их результат на консоль. Если вы не знакомы с потоками (streams) Java 8, смотрите мое руководство.

InvokeAny

Другой способ отдать на выполнение несколько задач — метод invokeAny() . Он работает немного по-другому: вместо возврата Future он блокирует поток до того, как завершится хоть одна задача, и возвращает ее результат.

Чтобы показать, как работает этот метод, создадим метод, эмулирующий поведение различных задач. Он будет возвращать Callable , который вернет указанную строку после необходимой задержки:

Читать еще:  Java io notserializableexception

Используем этот метод, чтобы создать несколько задач с разными строками и задержками от одной до трех секунд. Отправка этих задач исполнителю через метод invokeAny() вернет результат задачи с наименьшей задержкой. В данном случае это «task2»:

В примере выше использован еще один вид исполнителей, который создается с помощью метода newWorkStealingPool() . Этот метод появился в Java 8 и ведет себя не так, как другие: вместо использования фиксированного количества потоков он создает ForkJoinPool с определенным параллелизмом (parallelism size), по умолчанию равным количеству ядер машины.

ForkJoinPool впервые появился в Java 7, и мы рассмотрим его подробнее в следующих частях нашего руководства. А теперь давайте посмотрим на исполнители с планировщиком (scheduled executors).

Исполнители с планировщиком

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

ScheduledExecutorService способен запускать задачи один или несколько раз с заданным интервалом.

Этот пример показывает, как заставить исполнитель выполнить задачу через три секунды:

Когда мы передаем задачу планировщику, он возвращает особый тип Future — ScheduledFuture , который предоставляет метод getDelay() для получения оставшегося до запуска времени.

У исполнителя с планировщиком есть два метода для установки задач: scheduleAtFixedRate() и scheduleWithFixedDelay() . Первый устанавливает задачи с определенным интервалом, например, в одну секунду:

Кроме того, он принимает начальную задержку, которая определяет время до первого запуска.

Обратите внимание, что метод scheduleAtFixedRate() не берет в расчет время выполнения задачи. Так, если вы поставите задачу, которая выполняется две секунды, с интервалом в одну, пул потоков рано или поздно переполнится.

В этом случае необходимо использовать метод scheduleWithFixedDelay() . Он работает примерно так же, как и предыдущий, но указанный интервал будет отсчитываться от времени завершения предыдущей задачи.

В этом примере мы ставим задачу с задержкой в одну секунду между окончанием выполнения задачи и началом следующей. Начальной задержки нет, и каждая задача выполняется две секунды. Так, задачи будут запускаться на 0, 3, 6, 9 и т. д. секунде. Как видите, метод scheduleWithFixedDelay() весьма полезен, если мы не можем заранее сказать, сколько будет выполняться задача.

Это была первая часть серии статей про многопоточное программирование. Настоятельно рекомендую разобрать вышеприведенные примеры самостоятельно. Все они доступны на GitHub. Можете смело форкать репозиторий и добавлять его в избранное.

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

Пакет java.lang

Системные классы

Следующие классы, которые будут рассмотрены, обеспечивают взаимодействие с внутренними механизмами JVM и средой исполнения приложения:

  • ClassLoader – загрузчик классов; отвечает за загрузку описания классов в память JVM;
  • SecurityManager – менеджер безопасности; содержит различные методы проверки допустимости запрашиваемой операции;
  • System – содержит набор полезных статических полей и методов;
  • Runtime – позволяет приложению взаимодействовать со средой исполнения;
  • Process – представляет интерфейс для взаимодействия с внешней программой, запущенной при помощи Runtime .

ClassLoader

Это абстрактный класс, ответственный за загрузку типов. По имени класса или интерфейса он находит и загружает в память данные, которые составляют определение типа. Обычно для этого используется простое правило: название типа преобразуется в название class -файла, из которого и считывается вся необходимая информация.

Каждый объект Class содержит ссылку на объект ClassLoader , с помощью которого он был загружен.

Для добавления альтернативного способа загрузки классов можно реализовать свой загрузчик, унаследовав его от ClassLoader . Например, описание класса может загружаться через сетевое соединение. Метод defineClass() преобразует массив байт в экземпляр класса Class . С помощью метода newInstance() могут быть получены экземпляры такого класса. В результате загруженный класс становится полноценной частью исполняемого Java-приложения.

Для иллюстрации приведем пример, как может выглядеть простая реализация загрузчика классов, использующего сетевое соединение:

В этом примере только показано, что наследник загрузчика классов должен определить и реализовать методы findClass() и loadClassData() для загрузки описания класса. Когда описание получено, массив байт передается в метод defineClass() для создания экземпляра Class . Для простоты в примере приведен только шаблонный код, без реализации получения байт из сетевого соединения.

Для получения экземпляров классов, загруженных с помощью этого загрузчика, можно воспользоваться методом loadClass() :

Если такой класс не будет найден, будет брошено исключение ClassNotFoundException , если класс будет найден, но произойдет какая-либо ошибка при создании объекта этого класса – будет брошено исключение InstantiationException , и, наконец, если у вызывающего потока не имеется соответствующих прав для создания экземпляров этого класса (что проверяется менеджером безопасности), будет брошено исключение IllegalAccessException .

SecurityManager – менеджер безопасности

С помощью методов этого класса приложения перед выполнением потенциально опасных операций проверяют, является ли операция допустимой в данном контексте.

Класс SecurityManager содержит много методов с именами, начинающимися с приставки check («проверить»). Эти методы вызываются из стандартных классов библиотек Java перед тем, как в них будут выполнены потенциально опасные операции. Типичный вызов выглядит примерно следующим образом:

где X – название потенциально опасной операции: Access , Read , Write , Connect , Delete , Exec , Listen и т.д.

Предотвращение вызова производится путем бросания исключения – SecurityException , если вызов операции не разрешен (кроме метода checkTopLevelWindow , который возвращает boolean значение).

Для установки менеджера безопасности в качестве текущего вызывается метод setSecurityManager() в классе System . Соответственно, для его получения нужно вызвать метод getSecurityManager() .

В большинстве случаев, если приложение запускается локально, будут разрешены все действия, поскольку в системе SecurityManager отсутствует. Предполагается, что запускаемому локально приложению можно полностью доверять. Если же приложение может быть опасно (например, его код был загружен из сети, как это происходит в случае апплетов), то менеджер безопасности выставляется и его уже нельзя убрать или заменить (попытки вызовут SecurityException ). Он контролирует работу с локальной файловой системой, сетевыми соединениями, потоками исполнения и т.д.

System

Класс System содержит набор полезных статических методов и полей. Экземпляр этого класса не может быть создан или получен.

Читать еще:  Java nio charset malformedinputexception

Пожалуй, наиболее широко используемой возможностью, предоставляемой System , является стандартный вывод, доступный через переменную System.out . Ее тип – PrintStream (потоки данных будут подробно рассматриваться в лекции 15). Стандартный вывод можно перенаправить в другой поток (файл, массив байт и т.д., главное, чтобы это был объект PrintStream ):

При запуске этого кода на экран будет выведено только

Выполнение приложения Java в отдельном процессе

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

Я знаю, что вы можете выполнить программы через .

. основная проблема этого метода заключается в том, что такие вызовы являются специфичными для платформы.

В идеале, я бы обернуть метод в качестве.

. и передать полное имя класса приложения как CLASS_TO_BE_EXECUTED .

9 ответов

System.getProperty(«java.home») + «/bin/java» дает вам путь к исполняемому файлу java.

((URLClassLoader() Thread.currentThread().getContextClassLoader()).getURL() помогает восстановить путь к классам текущего приложения.

затем ваш EXECUTE.application просто (псевдокод):

Process.exec(javaExecutable, «-classpath», urls.join(«:»), CLASS_TO_BE_EXECUTED)

это синтез некоторых других ответов, которые были предоставлены. Системные свойства Java предоставляют достаточно информации, чтобы придумать путь к команде java и пути к классам, что, я думаю, является независимым от платформы способом.

вы бы запустили этот метод так:

Я думал, что имеет смысл передать фактический класс, а не строковое представление имени, так как класс должен быть в пути к классам в любом случае для это сработает.

расширение на ответ @stepancheg фактический код будет выглядеть так (в форме теста).

Это может быть излишним для вас, но Новый Проект делает то, что вы хотите, и больше. Я нашел его через запись в Kohsuke (один из рок-программистам начать Солнца) сказочно полезный блог.

вы проверили API ProcessBuilder? Он доступен с 1.5

вы действительно должны запустить их изначально? Не могли бы вы просто назвать их «основные» методы напрямую? Единственное, что особенное в main — это то, что VM launcher называет его, ничто не останавливает вас от вызова main самостоятельно.

следуя тому, что TofuBeer должен был сказать: вы уверены, что вам действительно нужно раскошелиться на другую JVM? JVM имеет действительно хорошую поддержку параллелизма в эти дни, поэтому вы можете получить много функциональности для относительно дешевых, просто откручивая Новый Поток или два (которые могут или не могут потребовать вызова Foo#main(String[])). Проверьте java.утиль.одновременно для получения дополнительной информации.

Если вы решите раскошелиться, вы настроите себя на немного сложности, связанные с поиском необходимых ресурсов. То есть, если ваше приложение часто меняется и зависит от кучи файлов jar, вам нужно будет отслеживать их все, чтобы они могли быть переданы в arg classpath. Кроме того, такой подход требует, чтобы вывести как местоположение (в настоящее время выполняющегося) JVM (которое может быть неточным), так и местоположение текущего пути к классам (что еще менее вероятно, будет точным, в зависимости от того, как был вызван нерестовый поток — jar, jnlp, взорвался .классы реж, некоторые контейнер, etc.).

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

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

чтобы обойти это, вы должны запустить Java.exe через » cmd.exe» и «старт». Я не знаю, почему, но если вы поставите «cmd / c start» перед ним, он покажет командную строку по мере ее запуска.

однако проблема с «пуском» заключается в том, что если в пути к приложению есть пробел (который путь к Java exe обычно имеет как есть в C:Program файлыJavajre6binjava.exe или похожие), тогда начать просто не удается с «не удается найти c:Program»

таким образом, вы должны поставить кавычки вокруг C:Program файлыJavajre6binjava.исполняемый Теперь начните жаловаться на параметры, которые вы передаете java.исполняемый: «Система не может найти файл -СР.»

экранирование пространства в «Program Files» с обратной косой чертой также не работает. Идея в том, чтобы не использовать пространство. Создайте временный файл с расширением bat, а затем поместите команда с пробелами внутри и запустить биту. Однако запуск летучей мыши через start, не выходит, когда сделано, так что вы должны поставить «выход» в конце пакетного файла.

Это все еще кажется неприятным.

Итак, ища альтернативы, я обнаружил, что использование кавычки space quote в пространстве «Program Files» на самом деле работает с start.

в классе EXECUTE выше измените строковый конструктор на:

Java ProcessBuilder tutorial

Java ProcessBuilder tutorial shows how to create operating system processes with ProcessBuilder . Java tutorial is a comprehensive tutorial on Java language.

Java ProcessBuilder

ProcessBuilder is used to create operating system processes. Its start() method creates a new Process instance with the following attributes:

  • command
  • environment
  • working directory
  • source of input
  • destination for standard output and standard error output
  • redirectErrorStream

Java ProcessBuilder running program

A program is executed with command() . With waitFor() we can wait for the process to finish.

The program executes the Windows Notepad application. It returns its exit code.

Java ProcessBuilder command output

The following example executes a command and shows its output.

The example runs Linux cal command.

The command() executes the cal program. The other parameters are the options of the program. In order to run a command on Windows machine, we could use the following: processBuilder.command(«cmd.exe», «/c», «ping -n 3 google.com») .

The process is lauched with start() .

With the getInputStream() method we get the input stream from the standard output of the process.

This is the ouptput.

Java ProcessBuilder redirect output

With redirectOutput() , we can redirect the process builder’s standard output destination.

The program redirects the builder’s output to a file. It runs the Windows date command.

We redirect the process builders standard output to a file.

Now the output goes to the file.

The current date was written to the output.txt file.

Читать еще:  Java string tochararray

Java ProcessBuilder redirect input and output

The next example redirects both input and output.

This are the contents of the input.txt file.

In the program, we redirect input from an input.txt file to the cat command and redirect the command’s output to the output.txt file.

Java ProcessBuilder inherit IO

The inheritIO() sets the source and destination for subprocess standard I/O to be the same as those of the current Java process.

By inheriting the IO of the executed command, we can skip the reading step. The program outputs the contents of the project directory and the message showing the exit code.

We get both the output of the executed command and of our own Java program.

Java ProcessBuilder environment

The environment() method returns a string map view of the process builder’s environment.

The program shows all environment variables.

This is a sample output on Windows.

In the next program, we define a custom environment variable.

The program defines a mode variable and outputs it on Windows.

The %mode% is a Windows syntax for environment variables; on Linux we use $mode .

Java ProcessBuilder directory

The directory() method sets the process builder’s working directory.

The example sets the home directory to be the process builder’s current directory. We show the contents of the home directory.

We get the user’s home directory.

We define a command which executes the dir program on Windows.

We set the process builder’s directory.

This is a sample output.

Java ProcessBuilder non-blocking operation

In the following example, we create a process which is asynchronous.

The program creates a process that runs the ping command on the console. It is executed in a separate thread with the help of the Executors.newSingleThreadExecutor() method.

This is the output.

Java ProcessBuilder pipe operation

A pipe is a technique for passing information from one program process to another.

The example sends information from a dir commmand to the grep command through the pipe (|).

This is the output.

In this tutorial, we have used Java’s ProcessBuilder to execute OS processes.

Java Process Example

Startup a native process from Java using Runtime. Read the output from the process and write some data to it. After you are done, terminate the process.

1. Introduction

Have you ever run into a situation where you need to execute an OS command from inside java and read its output? You can use the Process class to do so, but there are some caveats to consider.

2. Creating a Process

First of all, the simplest way to run an OS command in Java is to use Runtime.exec(). The method returns a Process object which can then be used to read its output, among other things.

The following example runs env in a process and reads the output written by the process and shows it on the console. It then waits for the process to exit using waitFor().

3. Specify Process Environment for Security

As you can see, when invoked as above the process receives all the environment variables of the invoking process. In general, that is not such a good idea because it can reveal a whole lot about the invoking process. Especially if you follow the 12-factor app guidelines, some of your passwords might be visible in the environment! These would then be available to the invoked process. To avoid this, follow a rule of “need-to-know”. In other words, pass only the variables that the invoked command needs to do its job.

In general, you should prefer the following invocation in preference to the above.

If you need to pass some explicit values for environment variables, use the following:

Environment variables are passed to the process without any interpretation whatsoever. In other words, shell variables (on Unix/Linux) are not processed in any special way.

4. Read Lines from Process

Use the Process.getInputStream() to set up a Reader to read from the target process. For example, the following reads and echoes lines with a line number from the child process.

5. Writing Data to Process

In addition to being to read from a process, you can use the Process.getOutputStream() to write data (text or binary) to the process. The next example writes some binary data to the process which computes a SHA256 sum and writes the output. Subsequently the program reads the output and prints it.

While the above code writes and reads from the child in a single thread, this is generally not a good idea. It works for this simple case but for more complex scenarios, you need to use separate threads for the read and write blocks. We discuss this in the next part of this guide.

6. Terminating a Process

Starting up a child process is all fine and dandy, now how about terminating it? You can use Process.destroy() to request termination nicely and Process.destroyForcibly() to kill a child process.

In the following example code, a child process is started with “cat” which just waits for the parent process to write something. However, the code below does not write anything and just attempts to read data. Which is not going to happen. So we have a separate thread which waits for 5 secs and attempts to destroy the child. And if that does not work, it kills the child process. Adios, son!

Summary

Runtime can be used to create a native OS process from inside java. Input and output streams are provided for reading its output and writing data to it. Its environment should probably be explicitly set to avoid passing all parent environment variables. The process can be terminated at any time by calling destroy().

Ссылка на основную публикацию
ВсеИнструменты 220 Вольт
Adblock
detector