eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Partner – LambdaTest – NPI EA (cat= Testing)
announcement - icon

Distributed systems often come with complex challenges such as service-to-service communication, state management, asynchronous messaging, security, and more.

Dapr (Distributed Application Runtime) provides a set of APIs and building blocks to address these challenges, abstracting away infrastructure so we can focus on business logic.

In this tutorial, we'll focus on Dapr's pub/sub API for message brokering. Using its Spring Boot integration, we'll simplify the creation of a loosely coupled, portable, and easily testable pub/sub messaging system:

>> Flexible Pub/Sub Messaging With Spring Boot and Dapr

1. Introduction

In this tutorial, we’re going to take an in-depth look at the Process API, in contrast to a shallower look into how to use Process to execute a shell command.

The process that it refers to is an executing application. The Process class provides methods for interacting with these processes, including extracting output, performing input, monitoring the lifecycle, checking the exit status, and destroying (killing) it.

2. Using the Process Class for Compiling and Running a Java Program

First, let’s see an example to compile and run another Java program with the help of the Process API:

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

Thus, the applications of executing Java code within existing Java code are virtually limitless.

3. Creating Process

In general, our Java application can call upon any application that is running within our computer system, subject to Operating System restrictions.

Therefore, we can execute applications. So, let’s see what the different use cases we can run by utilizing the Process API are.

In short, the ProcessBuilder class allows us to create subprocesses within our application.

Let’s see a demo of opening a Windows-based Notepad application:

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

4. Destroying Process

The Process class also provides us with methods to destroy subprocesses or processes. However, how the application is killed is platform-dependent.

Next, let’s exemplify different use cases using practical examples.

4.1. Destroying a Process by Reference

Let’s say we’re using Windows OS and want to spawn the Notepad application and destroy it.

As before, we can create an instance of the Notepad application by using the ProcessBuilder class and the start() method. Then, we can call the destroy() method on our Process object.

4.2. Destroying a Process by ID

Typically, we can kill the running processes within our operating system that our application might not create. Be cautious while doing this, as unknowingly destroying a critical process can destabilize the operating system.

Firstly, we need to find out the process ID of the currently running process by checking the task manager and finding out the PID.

So, let’s see an example:

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

4.3. Destroying a Process by Force

When we execute the destroy() method, it kills the subprocess, as we saw earlier in the article. In the case when destroy() doesn’t work, we have the option of destroyForcibly().

Notably, we should always start with the destroy() method first. Subsequently, we can perform a quick check on the subprocess using the isAlive() method.

If it returns true then execute destroyForcibly():

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

5. Waiting for a Process to Complete

We also have two overloaded methods, through which we can ensure that we can wait for a process to be completed.

5.1. waitfor()

In short, when this method executes, it places the current execution process thread in a blocking-wait state until the subprocess terminates.

So, let’s see it in action:

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

As we can see, the current thread waits for the subprocess thread to end. Once the subprocess ends, the current thread will continue its execution.

5.2. waitfor(long timeOut, TimeUnit time)

Typically, executing this method will place the current execution process thread in a blocking-wait state until the subprocess either terminates or times out.

So, let’s see this in practice:

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

We can see from the above example that for the current thread to continue execution, it will keep on waiting for the subprocess thread to end, or if the specified time interval has elapsed.

When this method is executed, it will return a boolean value of true if the subprocess has exited or a boolean value of false if the wait time has elapsed before the subprocess exited.

5.3. IllegalMonitorStateException: Current thread is not owner of the lock!

IllegalMonitorStateException: Current thread is not owner of the lock! in Java’s java.lang.Process API usually arises when attempting to use methods like wait() or notify() on a Process object without properly acquiring its monitor lock. This can happen if we’re trying to use these methods from a different thread than the one that created the process, or if we aren’t using the synchronized keyword correctly.

We should avoid using wait() and notify() directly on a Process object, as it could result in an IllegalMonitorStateException:

@Test
void givenAProcess_whenUsingWaitWithoutSynchronization_thenExceptionThrown() {
    assertThrows(IllegalMonitorStateException.class, () -> {
        Process process = new ProcessBuilder("notepad.exe").start();
        process.wait();
    });
}

The Process.wait() method is inherited from java.lang.Object; therefore, it isn’t intended for managing the lifecycle of a process. Instead, we should use Process.waitFor() to wait for the process to complete:

@Test
void givenAProcess_whenUsingWaitFor_thenNoExceptionThrown() {
    // Code that interacts with a process should not throw IllegalMonitorStateException.
    assertDoesNotThrow(() -> {
        Process process = new ProcessBuilder("notepad.exe").start();
        int exitCode = process.waitFor();
    });
}

In summary, we can fix the IllegalMonitorStateException: Current thread is not owner of the lock! when dealing with java.lang.Process, by using Process.waitFor() to wait for the process to complete. We should avoid calling wait() or notify() directly on the Process object unless we have a clear need and are handling synchronization correctly.

6. exitValue()

When this method is run then the current thread won’t wait for the subprocess to get terminated or destroyed, however, it will throw an IllegalThreadStateException if the subprocess isn’t terminated.

Another way around, if the subprocess has been successfully terminated, then it will result in an exit value of the process. It can be any possible positive integer number.

So, let’s look at an example when the exitValue() method returns a positive integer when the subprocess has been terminated successfully:

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

7. isAlive()

Typically, when we’d like to perform business processing, which is subjective whether the process is alive or not, we can perform a quick check to find whether the process is alive or not, which returns a boolean value.

Let’s see a quick example of it:

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

8. Handling Process Streams

By default, the created subprocess does not have its own terminal or console. All its standard I/O (i.e., stdin, stdout, stderr) operations will be sent to the parent process. Thereby, the parent process can use these streams to feed input to and get output from the subprocess.

Consequently, this gives us a huge amount of flexibility as it gives us control over the input/output of our subprocess.

8.1. getErrorStream()

Interestingly, we can fetch the errors generated from the subprocess and then perform business processing.

After that, we can execute specific business processing checks based on our requirements.

Let’s see an example:

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

8.2. getInputStream()

We can also fetch the output generated by a subprocess and consume it within the parent process, thus allowing the sharing of information between the processes:

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

8.3. getOutputStream()

We can send input to a subprocess from a parent process:

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

8.4. Filter Process Streams

It’s a perfectly valid use case to interact with selective running processes.

The process class provides us with the facility to selectively filter running processes based on a certain predicate.

After that, we can perform business operations on this selective process set:

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

9. Java 9 Improvements

Java 9 introduced new options and methods for getting information about current and spawned processes. Let’s dive deeper and explore each feature in detail.

9.1. Current Java Process Information

We can now obtain a lot of information about the process via the Java API java.lang.ProcessHandle.Info:

  • The command used to start the process
  • The arguments of the command
  • The instant when the process is started
  • The total time spent by it and the user who created it

For example, here’s how we can do that:

private static void infoOfCurrentProcess() {
    ProcessHandle processHandle = ProcessHandle.current();
    ProcessHandle.Info processInfo = processHandle.info();

    log.info("PID: " + processHandle.pid());
    log.info("Arguments: " + processInfo.arguments());
    log.info("Command: " + processInfo.command());
    log.info("Instant: " + processInfo.startInstant());
    log.info("Total CPU duration: " + processInfo.totalCpuDuration());
    log.info("User: " + processInfo.user());
}

It’s important to note that java.lang.ProcessHandle.Info is a public interface defined within another interface, java.lang.ProcessHandle. The JDK provider (Oracle JDK, Open JDK, Zulu, or others) should implement these interfaces in a way that returns the relevant information for the processes.

The output depends on the operating system and Java version. Here’s an example of what the output can look like:

16:31:24.784 [main] INFO  c.b.j.process.ProcessAPIEnhancements - PID: 22640
16:31:24.790 [main] INFO  c.b.j.process.ProcessAPIEnhancements - Arguments: Optional[[Ljava.lang.String;@2a17b7b6]
16:31:24.791 [main] INFO  c.b.j.process.ProcessAPIEnhancements - Command: Optional[/Library/Java/JavaVirtualMachines/jdk-13.0.1.jdk/Contents/Home/bin/java]
16:31:24.795 [main] INFO  c.b.j.process.ProcessAPIEnhancements - Instant: Optional[2021-08-31T14:31:23.870Z]
16:31:24.795 [main] INFO  c.b.j.process.ProcessAPIEnhancements - Total CPU duration: Optional[PT0.818115S]
16:31:24.796 [main] INFO  c.b.j.process.ProcessAPIEnhancements - User: Optional[username]

9.2. Spawned Process Information

It’s also possible to get the process information of a newly spawned process. In this case, after we spawn the process and get an instance of the java.lang.Process, we invoke the toHandle() method on it to get an instance of java.lang.ProcessHandle.

The rest of the details remain the same as in the section above:

String javaCmd = ProcessUtils.getJavaCmd().getAbsolutePath();
ProcessBuilder processBuilder = new ProcessBuilder(javaCmd, "-version");
Process process = processBuilder.inheritIO().start();
ProcessHandle processHandle = process.toHandle();

9.3. Enumerating Live Processes in the System

We can list all the processes currently in the system that are visible to the current process. The returned list is a snapshot of the API invoked, so it’s possible that some processes terminated after taking the snapshot or that new processes were added.

To do that, we can use the static method allProcesses() available in the java.lang.ProcessHandle interface, which returns us a Stream of ProcessHandle:

private static void infoOfLiveProcesses() {
    Stream<ProcessHandle> liveProcesses = ProcessHandle.allProcesses();
    liveProcesses.filter(ProcessHandle::isAlive)
        .forEach(ph -> {
            log.info("PID: " + ph.pid());
            log.info("Instance: " + ph.info().startInstant());
            log.info("User: " + ph.info().user());
        });
}

9.4. Enumerating Child Processes

There are two variants to do this:

  • Get the direct children of the current process
  • Get all the descendants of the current process

The former is achieved by using the method children() and the latter is achieved by using the method descendants():

private static void infoOfChildProcess() throws IOException {
    int childProcessCount = 5;
    for (int i = 0; i < childProcessCount; i++) {
        String javaCmd = ProcessUtils.getJavaCmd()
          .getAbsolutePath();
        ProcessBuilder processBuilder
          = new ProcessBuilder(javaCmd, "-version");
        processBuilder.inheritIO().start();
    }

    Stream<ProcessHandle> children = ProcessHandle.current()
      .children();
    children.filter(ProcessHandle::isAlive)
      .forEach(ph -> log.info("PID: {}, Cmd: {}", ph.pid(), ph.info()
        .command()));
    Stream<ProcessHandle> descendants = ProcessHandle.current()
      .descendants();
    descendants.filter(ProcessHandle::isAlive)
      .forEach(ph -> log.info("PID: {}, Cmd: {}", ph.pid(), ph.info()
        .command()));
}

9.5. Triggering Dependent Actions on Process Termination

We might want to run something on the termination of the process. This can be achieved by using the onExit() method in the java.lang.ProcessHandle interface. The method returns us a CompletableFuture, which provides the ability to trigger dependent operations when the CompletableFuture is completed.

Here, the CompletableFuture indicates the process has completed, but it doesn’t matter if the process has completed successfully or not. We invoke the get() method on the CompletableFuture, to wait for its completion:

private static void infoOfExitCallback() throws IOException, InterruptedException, ExecutionException {
    String javaCmd = ProcessUtils.getJavaCmd()
      .getAbsolutePath();
    ProcessBuilder processBuilder
      = new ProcessBuilder(javaCmd, "-version");
    Process process = processBuilder.inheritIO()
      .start();
    ProcessHandle processHandle = process.toHandle();

    log.info("PID: {} has started", processHandle.pid());
    CompletableFuture onProcessExit = processHandle.onExit();
    onProcessExit.get();
    log.info("Alive: " + processHandle.isAlive());
    onProcessExit.thenAccept(ph -> {
        log.info("PID: {} has stopped", ph.pid());
    });
}

The onExit() method is available in the java.lang.Process interface as well.

10. Conclusion

In this article, we covered most of the important features of the Process API in Java. Along the way, we discussed the new improvements introduced in Java 9.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

Course – LS – NPI (cat=Java)
announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

eBook Jackson – NPI EA – 3 (cat = Jackson)