How to create a process in Java
Categories:
How to Create and Manage Processes in Java

Learn to launch, interact with, and manage external processes from your Java applications using the ProcessBuilder and Process classes.
Java applications often need to interact with the operating system by executing external commands or programs. This capability is crucial for tasks like running shell scripts, invoking command-line utilities, or integrating with other software components. Java provides robust APIs, primarily ProcessBuilder
and Process
, to facilitate the creation, management, and communication with these external processes.
Understanding Java's Process API
The core of process management in Java revolves around two classes: ProcessBuilder
and Process
. ProcessBuilder
is used to configure and create new processes, offering fine-grained control over the process's environment, working directory, and I/O streams. Once a process is started, Process
represents the running external program, allowing your Java application to interact with its input, output, and error streams, wait for its completion, and retrieve its exit status.
flowchart TD A[Java Application] --> B{ProcessBuilder.start()} B --> C[New OS Process] C -- stdout --> D[Java InputStream] C -- stderr --> E[Java InputStream] F[Java OutputStream] -- stdin --> C C -- exit code --> G[Java Process.waitFor()] G --> H[Process Completed]
Flow of creating and interacting with an external process from Java.
Creating and Running a Simple Process
The simplest way to create a process is by using ProcessBuilder
to specify the command and its arguments. The start()
method then launches the process. It's crucial to handle the process's output and error streams to prevent the external process from blocking due to full buffers. This often involves reading from getInputStream()
and getErrorStream()
in separate threads.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
public class SimpleProcess {
public static void main(String[] args) {
try {
ProcessBuilder pb = new ProcessBuilder("ls", "-l"); // Example: list files in current directory
// For Windows: new ProcessBuilder("cmd.exe", "/c", "dir");
Process process = pb.start();
// Read output from the process
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// Wait for the process to complete and get its exit value
int exitCode = process.waitFor();
System.out.println("\nExited with error code : " + exitCode);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
Example of creating and running a simple 'ls -l' (or 'dir' on Windows) command.
Advanced Process Configuration and Interaction
Beyond simple execution, ProcessBuilder
allows for more advanced configurations. You can set the working directory, modify environment variables, and redirect standard I/O streams. Interacting with a process's input stream (your Java application's output stream) allows you to send data to the external program.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.IOException;
import java.util.Map;
public class AdvancedProcess {
public static void main(String[] args) {
try {
ProcessBuilder pb = new ProcessBuilder("grep", "hello"); // Example: search for 'hello'
// For Windows: new ProcessBuilder("findstr", "hello");
// Set working directory (optional)
// pb.directory(new File("/path/to/working/directory"));
// Modify environment variables (optional)
Map<String, String> env = pb.environment();
env.put("MY_VAR", "my_value");
Process process = pb.start();
// Write to the process's stdin
OutputStream os = process.getOutputStream();
os.write("hello world\n".getBytes());
os.write("goodbye world\n".getBytes());
os.close(); // Important: close the output stream to signal EOF to the external process
// Read output from the process
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println("Process Output: " + line);
}
// Read error output from the process (if any)
BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
while ((line = errorReader.readLine()) != null) {
System.err.println("Process Error: " + line);
}
int exitCode = process.waitFor();
System.out.println("\nExited with error code : " + exitCode);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
Example demonstrating setting environment variables and writing to a process's standard input.
OutputStream
, remember to close it (os.close()
) when you're done sending data. This signals the end-of-file (EOF) to the external process, allowing it to complete its input processing and potentially terminate.Handling Process Output Asynchronously
For long-running processes or those with substantial output, reading streams synchronously in the main thread can lead to deadlocks or performance issues. A common best practice is to read the InputStream
and ErrorStream
in separate threads. This allows your main application to continue execution while the process's output is being consumed concurrently.
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.Callable;
public class AsyncProcessOutput {
private static class StreamGobbler implements Callable<String> {
private final InputStream inputStream;
private final String streamType;
public StreamGobbler(InputStream inputStream, String streamType) {
this.inputStream = inputStream;
this.streamType = streamType;
}
@Override
public String call() throws IOException {
StringBuilder output = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String line;
while ((line = reader.readLine()) != null) {
output.append(streamType).append(": ").append(line).append("\n");
}
}
return output.toString();
}
}
public static void main(String[] args) {
try {
ProcessBuilder pb = new ProcessBuilder("bash", "-c", "echo 'Hello from stdout'; sleep 1; echo 'Error from stderr' >&2; sleep 1; echo 'Another stdout line'");
// For Windows: new ProcessBuilder("cmd.exe", "/c", "echo Hello from stdout && timeout /t 1 && echo Error from stderr 1>&2 && timeout /t 1 && echo Another stdout line");
Process process = pb.start();
// Consume streams in separate threads
Future<String> stdoutFuture = Executors.newSingleThreadExecutor().submit(new StreamGobbler(process.getInputStream(), "STDOUT"));
Future<String> stderrFuture = Executors.newSingleThreadExecutor().submit(new StreamGobbler(process.getErrorStream(), "STDERR"));
int exitCode = process.waitFor();
System.out.println("\nProcess STDOUT:\n" + stdoutFuture.get());
System.out.println("\nProcess STDERR:\n" + stderrFuture.get());
System.out.println("Exited with error code : " + exitCode);
} catch (IOException | InterruptedException | java.util.concurrent.ExecutionException e) {
e.printStackTrace();
}
}
}
Using StreamGobbler
with ExecutorService
to asynchronously read process output and error streams.