Java > Concurrency and Multithreading > Thread Basics > Thread Lifecycle

Thread Interruption and Joining

This example demonstrates how to interrupt a running thread and how to wait for a thread to complete its execution using `join()`. These mechanisms are essential for managing threads and ensuring proper program termination.

Code Snippet: Thread Interruption and Joining

This code creates a worker thread that performs a task until it's interrupted. The main thread starts the worker thread, waits for 2 seconds, interrupts the worker thread, and then waits for the worker thread to complete its execution using `join()`. The `isInterrupted()` method is used within the worker thread's loop to check for interruption. The `join()` method blocks the calling thread until the specified thread terminates.

public class ThreadInterruptJoin {

    public static void main(String[] args) throws InterruptedException {
        Thread workerThread = new Thread(() -> {
            try {
                System.out.println("Worker thread started");
                while (!Thread.currentThread().isInterrupted()) {
                    // Simulate some work
                    System.out.println("Working...");
                    Thread.sleep(500); // Simulate work taking time
                }
                System.out.println("Worker thread interrupted");
            } catch (InterruptedException e) {
                System.out.println("Worker thread interrupted by exception");
                Thread.currentThread().interrupt(); // Restore interrupted status
            } finally {
                System.out.println("Worker thread exiting");
            }
        });

        workerThread.start();

        // Let the worker thread run for a while
        Thread.sleep(2000);

        System.out.println("Interrupting worker thread");
        workerThread.interrupt();

        System.out.println("Joining worker thread");
        workerThread.join(); // Wait for the worker thread to finish

        System.out.println("Main thread finished");
    }
}

Concepts Behind the Snippet

Thread Interruption: A mechanism for signaling a thread to stop its current activity. It doesn't forcibly terminate the thread but sets a flag that the thread can check to determine if it should stop working. It involves calling `interrupt()` on the thread. Within the thread’s run method, the thread should periodically check `isInterrupted()` or `Thread.interrupted()` or handle `InterruptedException`.
Thread Joining: Allows one thread to wait for the completion of another thread. Calling `join()` on a thread blocks the calling thread until the target thread completes its execution. This is useful for ensuring that resources are properly cleaned up and that dependent operations are executed in the correct order.

Real-Life Use Case

In a download manager, you might have a thread responsible for downloading a file. The user might click a 'Cancel' button, which interrupts the download thread. The main thread would then `join()` the download thread to ensure that all resources associated with the download are properly released before the application exits. Similarly, consider a long-running calculation. The user might need to interrupt the calculation mid-way, triggering a safe termination sequence within the calculation thread.

Best Practices

  • Handle `InterruptedException` Correctly: When catching `InterruptedException`, always restore the interrupted status of the thread using `Thread.currentThread().interrupt()` if you can't fully handle the interruption. This ensures that higher-level code can properly respond to the interruption.
  • Use `join()` with Timeout: Consider using the `join(long timeout)` method to prevent indefinite blocking in case the target thread hangs or deadlocks.
  • Avoid Busy-Waiting: Instead of continuously checking `isInterrupted()`, use blocking methods like `Thread.sleep()` or `wait()` that throw `InterruptedException` when the thread is interrupted.

Interview Tip

Be prepared to explain the difference between `interrupt()` and `stop()` (the latter being deprecated and unsafe). Emphasize the cooperative nature of thread interruption, where the thread being interrupted must cooperate by checking its interrupted status. Understand the implications of not handling `InterruptedException` correctly.

When to Use Them

Use `interrupt()` when:
- You need to signal a thread to stop its current activity gracefully.
- You want to avoid forcibly terminating a thread, which can lead to resource leaks or data corruption.
Use `join()` when:
- You need to wait for a thread to complete its execution before proceeding.
- You want to ensure that resources are properly cleaned up and that dependent operations are executed in the correct order.

Memory Footprint

The memory footprint of interrupting and joining threads is minimal. Interruption involves setting a boolean flag within the thread object. Joining involves storing a reference to the joined thread. The primary memory overhead comes from the threads themselves and the data they manipulate.

Alternatives

Alternatives to `interrupt()` and `join()` depend on the specific use case:

  • Cancellation Tokens (e.g., `java.util.concurrent.CancellationException`): Can be used for cooperative cancellation in asynchronous tasks.
  • CountDownLatch: Can be used to synchronize the completion of multiple threads.
  • CyclicBarrier: Can be used to synchronize the execution of multiple threads at a specific point.

Pros

  • Graceful Thread Termination: `interrupt()` allows threads to terminate gracefully, cleaning up resources and avoiding data corruption.
  • Synchronization: `join()` provides a simple mechanism for synchronizing the execution of threads.
  • Controlled Execution: `interrupt()` and `join()` provide control over the execution and termination of threads, leading to more predictable and reliable applications.

Cons

  • Cooperative Interruption: Thread interruption relies on the thread cooperating by checking its interrupted status. If the thread doesn't cooperate, it may not terminate as expected.
  • Potential for Deadlock: Improper use of `join()` can lead to deadlocks if threads are waiting for each other to complete.
  • `InterruptedException` Handling: Requires careful handling of `InterruptedException` to avoid losing the interruption signal.

FAQ

  • What happens if a thread is blocked in `Object.wait()` when `interrupt()` is called?

    The `wait()` method will throw an `InterruptedException`, allowing the thread to handle the interruption and exit gracefully.
  • Does `interrupt()` guarantee that the thread will stop immediately?

    No, `interrupt()` only sets the interrupted status of the thread. It's up to the thread to check its interrupted status and respond accordingly. If the thread is blocked in a blocking operation, `interrupt()` will typically throw an `InterruptedException`.
  • What is the difference between `isInterrupted()` and `Thread.interrupted()`?

    `isInterrupted()` is an instance method that checks the interrupted status of a specific thread and does not clear the interrupted flag. `Thread.interrupted()` is a static method that checks the interrupted status of the *current* thread and clears the interrupted flag. Use `isInterrupted()` when you need to check the interrupted status of a different thread and preserve the interrupt status for further handling. Use `Thread.interrupted()` primarily when you're handling the interruption and want to clear the flag.