This design ensures better control, avoids resource corruption, and prevents inconsistent program states. In this article, we will explore cooperative interruption, understand
InterruptedException, and learn the best practices for stopping threads.
Cooperative Interruption
In Java, thread cancellation is based on cooperation rather than force. Wheninterrupt() is called on a thread, it does not stop the thread immediately. Instead, it sets an internal interrupt flag.
The thread must periodically check this flag and decide whether to terminate.
Thread t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Working...");
}
System.out.println("Thread stopping gracefully");
});
t.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.interrupt();
Output:
Working...
Working...
.
.
.
Working...
Thread stopping gracefully
In this example, the thread continuously runs until it detects the interrupt signal using isInterrupted(). Once detected, it exits the loop and terminates gracefully.
Key Idea:
- Interruption is a request, not a command- The thread decides how to respond
- This avoids unsafe termination
InterruptedException
Certain methods in Java, such assleep(), wait(), and join(), are blocking operations. When a thread is interrupted while executing one of these methods, Java throws an InterruptedException.
Thread t = new Thread(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println("Interrupted during sleep");
}
});
t.start();
t.interrupt();
Output:
Interrupted during sleep
Here, the thread is sleeping, and when interrupt() is called, the sleep is interrupted and an exception is thrown.
When
InterruptedException is thrown:
- The thread is woken up from a blocking state (like
sleep(), wait(), or join())- The interrupt flag is cleared
- Control moves to the
catch block
But the thread continues executing after the catch block unless you explicitly stop it.
Important Behavior:
WhenInterruptedException is thrown, Java clears the interrupt flag as part of the exception handling mechanism. This means that after catching the exception, the thread no longer appears to be interrupted.
This becomes a problem because higher-level code may rely on that interrupt signal to make decisions (like stopping execution or cleaning up resources). If you donβt restore the flag, that information is effectively lost.
Thread t = new Thread(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();// restore interrupt flag
}
});
t.start();
t.interrupt();
This ensures that higher-level code is aware of the interruption.
Best Practices for Stopping Threads
Stopping threads incorrectly can lead to serious issues such as data corruption, deadlocks, and resource leaks. The following best practices should always be followed.1. Never Use stop()
TheThread.stop() method is deprecated and unsafe because it terminates a thread abruptly without giving it a chance to release resources or complete operations.
//Do NOT use t.stop();
2. Use interrupt() for Cancellation
Always useinterrupt() to request thread termination. A well-written thread is designed like this:
Thread t = new Thread(() -> {
try {
while (!Thread.currentThread().isInterrupted()) { // do work
Thread.sleep(1000);
}
} catch (InterruptedException e) { // interruption during sleep
Thread.currentThread().interrupt(); // restore flag
}
System.out.println("Thread exiting...");
});
Now when you call t.interrupt(); the thread will: :
- Exit loop (because flag is set) OR - Catch exception (if sleeping) - Then finish execution β TERMINATED
This allows the thread to clean up resources and exit safely.
3. Check Interrupt Status Regularly
Threads performing long-running tasks should periodically check their interrupt status. while (!Thread.currentThread().isInterrupted()) {
// perform work
}
4. Handle InterruptedException Properly
Do not ignoreInterruptedException. Either handle it or re-interrupt the thread.
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
5. Use Volatile Flag (Alternative Approach)
Sometimes, a custom flag is used to control thread execution.class Worker implements Runnable {
private volatile boolean running = true;
public void stop() {
running = false;
}
public void run() {
while (running) {
System.out.println("Working...");
}
}
static void main() {
var worker = new Worker();
Thread t = new Thread(worker);
t.start();
try {
Thread.sleep(1000); // Let the worker run for a bit
worker.stop(); // Signal the worker to stop
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
However, this approach does not handle blocking calls, so interrupt() is generally preferred. Because a simple volatile flag only works when the thread is actively running, but it completely fails when the thread is stuck in a blocking call like sleep(), wait(), or join().
6. Combine interrupt() with Cleanup
Always ensure that resources such as files, sockets, or locks are released properly when a thread stops. Thread t = new Thread(() -> {
try {
while (!Thread.currentThread().isInterrupted()) { // work
}
} finally {
System.out.println("Cleaning up resources...");
}
});
Key Insights
- Java uses cooperative interruption instead of forced termination-
interrupt() sets a flag, not stops execution- Blocking methods throw
InterruptedException- Proper handling ensures safe and predictable thread termination
Thread interruption and cancellation are fundamental to writing safe and robust multithreaded applications. By adopting a cooperative approach, Java ensures that threads terminate gracefully without compromising system stability.
Mastering
interrupt(), understanding InterruptedException, and following best practices will help you build systems that are both efficient and reliable. In the next article, we will explore synchronization and how threads safely share resources
Join the discussion