Back to Blog
Java

Java Thread Interrupt: How It Works and How to Use It

java thread interrupt: Learn how Java thread interrupt works, how to handle InterruptedException, and how to use interrupt flags for cooperative cancellation in real-w...

threadingconcurrencyinterruptmultithreadingexecutor-service
A thread being interrupted with a flag, representing Java's cooperative cancellation mechanism.

The java thread interrupt mechanism is a cooperative way to signal a thread that it should stop what it is doing. It does not force a thread to terminate; instead, it sets a flag that the thread can check and react to. This design gives the developer full control over when and how a thread stops, avoiding the unsafe and deprecated Thread.stop() method.

What Does Thread.interrupt() Actually Do?

When you call interrupt() on a Thread instance, the JVM sets an internal flag on that thread. The flag is not automatically cleared unless the thread checks it or a blocking method throws an InterruptedException. The interrupted thread can observe the flag in two ways:

  • By calling Thread.interrupted() (static method), which returns the flag and clears it.
  • By calling isInterrupted() on the thread instance, which returns the flag without clearing it.

If the thread is currently blocked in a method that throws InterruptedException (such as Thread.sleep(), Object.wait(), or BlockingQueue.take()), that method immediately throws InterruptedException and clears the flag. If the thread is busy computing, the flag remains set until the thread checks it.

public class InterruptExample { public static void main(String[] args) throws InterruptedException { Thread worker = new Thread(() -> { while (!Thread.currentThread().isInterrupted()) { // do some work } System.out.println("Worker stopped gracefully"); }); worker.start(); Thread.sleep(1000); worker.interrupt(); } }

Here, the worker thread checks the interrupt flag in its loop condition. When the main thread calls interrupt(), the loop exits and the thread finishes. This is a simple cooperative cancellation pattern.

The Role of InterruptedException

Many blocking methods in the Java standard library declare throws InterruptedException. When a thread is interrupted while blocked in such a method, the method throws the exception and clears the interrupt flag. This is important: the flag is consumed by the exception. If you catch the exception but do not restore the flag, the thread loses the interrupt signal.

The recommended practice is to either re-interrupt the thread or let the exception propagate. Never swallow the exception without re-interrupting, because the caller or the thread's own logic may rely on the interrupt status.

public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { // Restore the interrupt status Thread.currentThread().interrupt(); // Optionally handle the cancellation } }

By calling Thread.currentThread().interrupt() in the catch block, you set the flag again. This allows higher-level code to see that the thread was interrupted and can react appropriately.

Checking the Interrupt Status with isInterrupted()

The isInterrupted() instance method is non-static and does not clear the flag. It is useful when you need to check the flag without altering it. For example, in a loop that performs a long computation, you can check isInterrupted() at safe points and break out.

public void compute() { while (!Thread.currentThread().isInterrupted()) { // long-running calculation if (someCondition) { break; } } }

The static Thread.interrupted() method, on the other hand, clears the flag. It is often used in code that handles the interrupt itself and does not want to propagate it further. Use it carefully, because clearing the flag may hide the fact that an interrupt occurred.

Cooperative Cancellation: A Practical Pattern

A common use of interrupts is to cancel a worker thread that is processing tasks. The worker checks the interrupt flag between tasks or within its main loop. This pattern is cooperative because the worker must be designed to respond to the interrupt.

class Worker implements Runnable { public void run() { while (!Thread.currentThread().isInterrupted()) { processNextTask(); } } private void processNextTask() { // do something } }

To stop the worker, call workerThread.interrupt(). The worker will finish the current task and then exit the loop. This is safe because the worker controls when it stops, avoiding partial state corruption.

Handling InterruptedException in Blocking Calls

When you call a method like Thread.sleep() or BlockingQueue.take(), an interrupt causes the method to throw InterruptedException. The correct handling depends on whether you can propagate the exception or must handle it locally.

If you are implementing Runnable.run(), you cannot declare checked exceptions, so you must catch InterruptedException. In that case, restore the interrupt flag and exit the method gracefully. If you are in a method that can declare throws InterruptedException, let it propagate to the caller, who can decide how to handle it.

public void processQueue(BlockingQueue<Job> queue) throws InterruptedException { while (true) { Job job = queue.take(); // throws InterruptedException process(job); } }

In this example, the method declares the exception. If the thread is interrupted while waiting for a job, take() throws, and the method exits. The caller can then decide whether to re-interrupt or take other action.

Interrupting Threads in Executor Services

When using an ExecutorService, you do not directly manage threads. Instead, you submit tasks and receive Future objects. To cancel a running task, you call future.cancel(true). This method attempts to interrupt the thread that is executing the task.

ExecutorService executor = Executors.newFixedThreadPool(2); Future<?> future = executor.submit(() -> { while (!Thread.currentThread().isInterrupted()) { // task work } }); // later future.cancel(true);

The cancel(true) call sends an interrupt to the worker thread. The task must be written to respond to interrupts for this to work. If the task ignores interrupts, cancellation will not stop it.

It is important to note that cancel(true) only interrupts the thread if the task is already running. If the task has not started, it may be cancelled without being run, depending on the executor's implementation.

Common Mistakes and How to Avoid Them

One common mistake is swallowing InterruptedException without restoring the flag. This breaks the cooperative cancellation contract. Always re-interrupt the thread if you cannot propagate the exception.

Another mistake is using Thread.stop() instead of interrupt. Thread.stop() is deprecated and unsafe because it can leave shared objects in an inconsistent state. Interrupt is the safe alternative.

A third mistake is checking the interrupt flag only at the beginning of a long-running computation. The thread may not notice an interrupt until the computation finishes. Check the flag periodically at safe points, especially if the computation takes a long time.

Finally, remember that Thread.interrupted() clears the flag. If you call it and then later check isInterrupted(), you will see false. Use the static method only when you intend to consume the interrupt signal.

Interrupting Threads in Virtual Threads

With Java 21, virtual threads are available. Virtual threads support interruption in the same way as platform threads. The same cooperative model applies: calling interrupt() on a virtual thread sets the flag, and blocking operations throw InterruptedException. This consistency makes it easy to apply the patterns described above to virtual threads.

Thread vThread = Thread.startVirtualThread(() -> { while (!Thread.currentThread().isInterrupted()) { // do work } }); // later vThread.interrupt();

Virtual threads are lightweight, but the interrupt semantics remain unchanged. This is a relief for developers who have learned the interrupt model and can reuse it without modification.

java thread interrupt: Practical Usage and Code Examples | RYUSLOG DEV