Java Programming Thread Lifecycle and Thread Class Step by step Implementation and Top 10 Questions and Answers
 Last Update:6/1/2025 12:00:00 AM     .NET School AI Teacher - SELECT ANY TEXT TO EXPLANATION.    25 mins read      Difficulty-Level: beginner

Java Programming: Thread Lifecycle and Thread Class

Java is a concurrent language, meaning it supports parallel execution of two or more parts of a program (threads) for maximum utilization of CPU resources. Understanding the lifecycle and usage of threads in Java, specifically through the Thread class, is crucial for developing efficient and responsive applications. In this article, we will delve deep into the lifecycle stages of a thread and explore key functionalities of the Thread class.

Thread Lifecycle

The lifecycle of a thread in Java can be broadly categorized into five states: New, Runnable, Blocked, Waiting, Timed Waiting, and Terminated. Each stage represents the thread's state as it progresses from creation to completion:

  1. New State

    • When a new thread object is created, it is in the new state.
    • Example: Thread thread = new Thread();
    • The thread does not run until explicitly told to by invoking the start() method.
  2. Runnable State

    • Once the start() method is called, the thread moves to the runnable state.
    • Example: thread.start();
    • Threads in this state are ready to run but may not be running immediately due to CPU scheduling.
    • The JVM places these threads into a thread pool called a run queue, where they wait for a CPU time slot.
  3. Running State

    • A thread enters the running state after CPU scheduling.
    • The run() method of the thread executes during this time.
    • Only one thread can execute at a given time on a single CPU.
  4. Blocked State

    • When a thread waits for a monitor lock to enter a synchronized block or method, it is in the blocked state.
    • It remains here until the lock is acquired.
    • Example:
      synchronized(lockObject) {
          // critical section
      }
      
  5. Waiting State

    • A thread enters the waiting state when it calls methods like wait(), join(), or park().
    • Unlike the blocked state, threads in the waiting state require another external signal to move to the runnable state.
    • Methods like notify()/notifyAll() can wake up a thread from the waiting state.
    • Example:
      object.wait();
      
  6. Timed Waiting State

    • This state occurs when a thread calls sleep(long millis), wait(long timeout), join(long millis), or await(long timeout, TimeUnit unit).
    • The thread remains in timed waiting for a specific period and then automatically transitions back to the runnable state.
    • Example:
      Thread.sleep(1000); // Sleep for 1000 milliseconds
      
  7. Terminated State

    • When a thread finishes executing its run() method, it enters the terminated state.
    • At this stage, a thread cannot be restarted; a new thread instance must be created.

Key Functionalities of the Thread Class

The Thread class in Java provides various methods that help manage threads effectively. Here are some important ones:

  1. Start Method (void start()):

    • This method is used to start the thread. It internally calls the run() method and makes the thread eligible for execution.
    • Example:
      Thread thread = new Thread(() -> {
          System.out.println("Thread Running...");
      });
      thread.start(); // starts the thread
      
  2. Run Method (void run()):

    • This method contains the code that a thread will execute once started.
    • You typically override this method in a subclass to specify the thread’s task.
    • Example:
      Thread thread = new Thread(() -> {
          System.out.println("Thread is running...");
      });
      // Alternatively,
      class MyThread extends Thread {
          public void run() {
              System.out.println("My Thread is running...");
          }
      }
      
  3. Sleep Method (static void sleep(long millis)):

    • Causes the currently executing thread to pause temporarily in milliseconds.
    • Can throw an InterruptedException if the thread is interrupted while sleeping.
    • Example:
      try {
          Thread.sleep(1000);
      } catch (InterruptedException ie) {
          System.out.println("Thread interrupted");
      }
      
  4. Join Method (void join()):

    • This method is called from one thread (T1) on a second thread (T2). It makes T1 to wait until T2 completes its execution.
    • Overloaded versions allow specifying a timeout: join(long millis) and join(long millis, int nanos).
    • Example:
      Thread t1 = new Thread(() -> {
          for(int i=0; i < 5; i++) {
              System.out.println(Thread.currentThread().getName());
          }
      });
      
      Thread t2 = new Thread(() -> {
          try {
              t1.join(); // wait until t1 completes
          } catch (InterruptedException ie) {}
          for(int i=0; i < 5; i++) {
              System.out.println(Thread.currentThread().getName());
          }
      });
      
      t1.start();
      t2.start();
      
  5. Interrupt Method (void interrupt()):

    • Interrupts a thread, indicating that it should stop what it is doing. It sets the thread's interrupt status to true.
    • Not all methods will respond to interruption. Some methods like wait() can throw an InterruptedException.
    • Example:
      Thread thread = new Thread(() -> {
          try {
              System.out.println("Thread is going into infinite loop...");
              while (true) {
                  Thread.sleep(100); // can be interrupted
              }
          } catch (InterruptedException ie){
              System.out.println("Thread interrupted");
          }
      });
      
      thread.start();
      
      // Main thread interrupts t1
      thread.interrupt();
      
  6. IsAlive Method (boolean isAlive()):

    • Checks if the thread is still alive, i.e., whether it has been started and has not yet completed and entered the terminated state.
    • Example:
      Thread thread = new Thread(() -> {
          // task here
      });
      
      thread.start();
      System.out.println(thread.isAlive()); // true
      
  7. Priority Setters and Getters (int getPriority() and void setPriority(int priority)):

    • Thread priority is set with values ranging from 1 (MIN_PRIORITY) to 10 (MAX_PRIORITY).
    • Threads with higher priorities are preferred by the scheduler for execution.
    • It is important to note that priority does not guarantee execution order; it only gives a hint to the JVM scheduler.
    • Example:
      Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
      System.out.println(Thread.currentThread().getPriority()); // 10
      
  8. Daemon Threads (void setDaemon(boolean on) and boolean isDaemon()):

    • Daemon threads are background threads that provide services to user threads. They have lower priority than user threads.
    • If all user threads terminate, the application exits (even if daemon threads are still running).
    • Daemon status must be set before the thread starts (start()). Otherwise, an IllegalThreadStateException will be thrown.
    • Example:
      Thread thread = new Thread(() -> {
          // daemon work
      });
      thread.setDaemon(true);
      thread.start();
      
  9. Name Management (String getName() and void setName(String name)):

    • Threads have names to easily identify them.
    • Naming conventions enhance debugging and thread management.
    • By default, the name is in the format "Thread-" + n, where n is a generated number.
    • Example:
      Thread thread = new Thread(() -> {});
      thread.setName("MyThread");
      System.out.println(thread.getName()); // MyThread
      
  10. Thread Local Storage (ThreadLocal<T>):

    • Provides variables that each thread in a multithreaded environment can access locally but not other threads.
    • Commonly used for managing transactional data in database applications.
    • Example:
      ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
      
      Thread thread = new Thread(() -> {
          threadLocal.set(100);
          Integer value = threadLocal.get(); // returns 100
          System.out.println(value);
      });
      
      thread.start();
      

Practical Example of Thread Lifecycle

Here is a simple example to illustrate the different states of a thread and the methods related to them:

class MyThread extends Thread {
    public void run() {
        System.out.println("Thread is in Running state");
        try {
            System.out.println("Going into Timed Waiting state");
            Thread.sleep(3000); // sleeps for 3 seconds
        } catch (InterruptedException e) {
            System.out.println("Thread was interrupted");
        }

        System.out.println("Thread has returned to Runnable state after sleep");

        // Simulating a critical section to cause blocking state
        synchronized(this) {
            try {
                wait(); // goes into waiting state
            } catch (InterruptedException e) {
                System.out.println("Waiting thread interrupted");
            }
        }

        System.out.println("Thread has terminated");
    }
}

public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread(); 
        System.out.println("Thread is in New state");

        thread.start();
        System.out.println("Thread has called start() method and is now in Runnable State");

        Thread.sleep(1000);
        System.out.println("Thread is still in Runnable state");

        // Letting the thread go into waiting state
        synchronized(thread) {
            thread.notify(); // wakes up the thread from waiting state
        }

        Thread.sleep(5000);
        
        System.out.println("Is Thread Alive? : " + thread.isAlive()); // false, as thread has completed its run()
    }
}

In this example, when thread.start(); is called, the thread transitions from the new state to the runnable state and ultimately to the running state where the run() method executes. After Thread.sleep(3000);, the thread enters a timed-waiting state. Inside the synchronized block, the thread calls wait(), placing it into the waiting state. Finally, after notifying the thread, it completes its task and moves into the terminated state.

Understanding these concepts provides developers with the tools needed to create robust, scalable, and high-performing applications using Java's multithreading capabilities. The Thread class plays a vital role in managing thread behavior, making it a central component in Java concurrency programming.




Java Programming: Thread Lifecycle and Thread Class

Understanding the lifecycle of a thread and how to work with Java's Thread class is fundamental to mastering concurrent programming in Java. Threads enable the execution of multiple operations concurrently within the same process, enhancing program efficiency and responsiveness, especially for applications that perform I/O operations or require parallel processing.

This tutorial will guide beginners through the steps needed to set up a simple route for threading, execute an application, and observe the data flow involved. We’ll break down the process into manageable steps, ensuring each concept is well-explained and practical.

Step 1: Setting Up Your Development Environment

Before diving into threads, make sure your Java development environment is ready:

  • Java Development Kit (JDK): Ensure you have the latest JDK installed. You can download it from the official Oracle website or use OpenJDK.
  • Integrated Development Environment (IDE): Use an IDE like IntelliJ IDEA, Eclipse, or even a simple text editor with the command line.

For simplicity, we'll use Eclipse IDE in this example.

Step 2: Creating a Basic Java Project

Let’s start by creating a basic Java project called ThreadLifecycleDemo.

  1. Open Eclipse and go to File > New > Java Project.
  2. Name your project ThreadLifecycleDemo and click Finish.

Step 3: Implementing a Simple Thread Class

In Java, you can create a thread by extending the Thread class or implementing the Runnable interface. Here, we’ll extend the Thread class for simplicity.

  1. Within your project, right-click on the src folder and select New > Class.
  2. Name the class MyThread and ensure it extends Thread.
  3. Override the run() method to include the logic you want to execute on a new thread.
// MyThread.java
public class MyThread extends Thread {
    
    // Constructor with an optional name for easier debugging
    public MyThread(String name) {
        super(name);
    }
    
    @Override
    public void run() {
        try {
            // Thread goes into Running state upon starting
            System.out.println(Thread.currentThread().getName() + " is running.");
            
            // Simulate a task taking time to complete
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + ": Count " + i);
                Thread.sleep(1000); // Sleep for 1 second
            }
            
            // Task completes and thread terminates naturally
            System.out.println(Thread.currentThread().getName() + " has finished running.");
        } catch (InterruptedException e) {
            // Handle Interruptions here
            System.out.println(Thread.currentThread().getName() + " was interrupted.");
            return;
        }
    }
}

Step 4: Understanding Thread Lifecycle

A thread in Java undergoes several states during its lifetime, which are:

  • New: The instance of Thread is created but not yet started.
  • Runnable: The thread is ready to run or currently executing a task.
  • Blocked: The thread is waiting to enter a synchronized block or method.
  • Waiting: The thread is waiting indefinitely until another thread wakes it up.
  • Timed Waiting: Similar to waiting, but the thread only waits for a specified period.
  • Terminated: The thread has completed execution either successfully or due to an exception.
Step 5: Setting the Route and Running the Application

To demonstrate the lifecycle of a thread, let's create a main class (ThreadLifecycleExample) that will instantiate and manage our custom thread.

  1. Right-click on the src folder and select New > Class.
  2. Name the class ThreadLifecycleExample, which will act as our main entry point.
// ThreadLifecycleExample.java
public class ThreadLifecycleExample {

    public static void main(String[] args) {
        // Create thread instances before calling start()
        MyThread threadOne = new MyThread("Thread-1");
        MyThread threadTwo = new MyThread("Thread-2");
        
        // Display the initial state of threads (New)
        System.out.println(threadOne.getName() + " state: " + threadOne.getState());
        System.out.println(threadTwo.getName() + " state: " + threadTwo.getState());

        // Start the threads, transitioning them to Runnable state
        threadOne.start();
        threadTwo.start();
        
        // Display the state of threads after calling start()
        System.out.println(threadOne.getName() + " state during execution: " + threadOne.getState());
        System.out.println(threadTwo.getName() + " state during execution: " + threadTwo.getState());

        // Wait for threads to finish execution. This blocks the main thread.
        try {
            threadOne.join(); 
            threadTwo.join();

        } catch (InterruptedException e) {
            // Log or handle the interruption
            System.out.println(Thread.currentThread().getName() + " was interrupted.");
        }

        // Verify the final state of threads (Terminated)
        System.out.println(threadOne.getName() + " state after finished: " + threadOne.getState());
        System.out.println(threadTwo.getName() + " state after finished: " + threadTwo.getState());
    }
}
  1. Run the application (Run > Run As > Java Application).

You should observe an output similar to the following:

Thread-1 state: NEW
Thread-2 state: NEW
Thread-1 is running.
Thread-2 is running.
Thread-1 state during execution: RUNNABLE
Thread-2 state during execution: RUNNABLE
Thread-1: Count 0
Thread-2: Count 0
Thread-1: Count 1
Thread-2: Count 1
...
Thread-1 has finished running.
Thread-2 has finished running.
Thread-1 state after finished: TERMINATED
Thread-2 state after finished: TERMINATED

Step 6: Observing Data Flow and State Transitions

Here is a comprehensive walk-through of what happens in each step:

  1. Creating the Thread Instance:

    • MyThread threadOne = new MyThread("Thread-1"); & MyThread threadTwo = new MyThread("Thread-2");
      • Upon instantiation, threads are in the NEW state. They haven’t started executing yet.
  2. Checking Initial State:

    • System.out.println(threadOne.getName() + " state: " + threadOne.getState());
    • System.out.println(threadTwo.getName() + " state: " + threadTwo.getState());
      • Outputs state of both threads as NEW.
  3. Starting the Thread:

    • threadOne.start(); & threadTwo.start();
      • Internally, start() calls the thread’s run() method. It transitions the state from NEW to RUNNABLE. The threads now start executing their run method concurrently.
  4. Displaying Initial Running State:

    • System.out.println(threadOne.getName() + " state during execution: " + threadOne.getState());
    • System.out.println(threadTwo.getName() + " state during execution: " + threadTwo.getState());
      • While our threads are still busy counting, they transition through RUNNABLE and possibly BLOCKED if the CPU context switches between threads.
  5. Simulating a Task:

    • Inside the run() method:
      for (int i = 0; i < 5; i++) {
          System.out.println(Thread.currentThread().getName() + ": Count " + i);
          Thread.sleep(1000);
      }
      
      • Each thread prints count values while sleeping for 1 second per loop iteration. At these sleep intervals, threads transition to TIMED_WAITING and then back to RUNNABLE.
  6. Terminating the Thread:

    • Once the loop completes, the threads terminate naturally, changing to the TERMINATED state.
  7. Join Method:

    • threadOne.join(); & threadTwo.join();
      • The main thread waits for both child threads to complete execution using the join() method, which makes the main thread enter the WAITING state until join() returns.

Step 7: Advanced Concept - Interrupting the Thread

What happens if you try to interrupt the thread during its execution? Let's modify ThreadLifecycleExample to demonstrate interruption.

// ThreadLifecycleExample.java modified for interruption demo
public class ThreadLifecycleExample {

    public static void main(String[] args) {
        MyThread threadOne = new MyThread("Thread-1");
        MyThread threadTwo = new MyThread("Thread-2");

        System.out.println(threadOne.getName() + " state: " + threadOne.getState());
        System.out.println(threadTwo.getName() + " state: " + threadTwo.getState());

        threadOne.start();
        threadTwo.start();

        System.out.println(threadOne.getName() + " state during execution: " + threadOne.getState());
        System.out.println(threadTwo.getName() + " state during execution: " + threadTwo.getState());

        try {
            Thread.sleep(2500); // Main thread sleeps for 2.5 seconds
            threadTwo.interrupt(); // Interrupts Thread-2 while it's sleeping

            threadOne.join();
            threadTwo.join();

        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName() + " was interrupted.");
        }

        System.out.println(threadOne.getName() + " state after join(): " + threadOne.getState());
        System.out.println(threadTwo.getName() + " state after join(): " + threadTwo.getState());

        // Check whether Thread-2 was actually interrupted
        if (threadTwo.isInterrupted()) {
            System.out.println(threadTwo.getName() + " was interrupted by main thread.");
        }
    }
}

When you run the modified application, you may see:

Thread-1 state: NEW
Thread-2 state: NEW
Thread-1 is running.
Thread-2 is running.
Thread-1 state during execution: RUNNABLE
Thread-2 state during execution: RUNNABLE
Thread-1: Count 0
Thread-2: Count 0
Thread-1: Count 1
Thread-2: Count 1
Thread-1: Count 2
Thread-2 was interrupted.
Thread-1: Count 3
Thread-1: Count 4
Thread-1 has finished running.
Thread-2 has finished running.
Thread-1 state after join(): TERMINATED
Thread-2 state after join(): TERMINATED
Thread-2 was interrupted by main thread.

This shows that when Thread-2 is sleeping, it's interrupted by the main thread, causing it to exit prematurely. The interruption is caught in Thread-2’s run() method and handled accordingly, demonstrating the use of interrupt() and isInterrupted() methods.

Conclusion

Through this step-by-step tutorial, we've walked through setting up a new Java project, implementing a custom thread class, observing the thread lifecycle, starting threads, running the application, and handling thread interruptions. Understanding these concepts helps you write efficient, responsive, and error-free multi-threaded Java applications. Practice creating and managing additional threads to deepen your knowledge!




Top 10 Questions and Answers on Java Programming: Thread Lifecycle and Thread Class

1. What is a Thread in Java, and why do we use it?

  • Answer: In Java, a thread is the smallest unit of a process that can execute independently. Java threads enable concurrent and parallel execution within an application, improving performance and responsiveness, especially in I/O-bound and network-bound applications. They allow multiple operations to run concurrently, reducing idle times and improving resource utilization.

2. What are the five states of a Java thread, and how do they transition?

  • Answer: The five states of a Java thread are: New, Runnable, Blocked/Waiting, Timed Waiting, and Terminated.

    • New: When a thread instance is created, it is in the New state.
    • Runnable: Once the start() method is called on the thread, it transitions to Runnable, indicating that it can be executed by the thread scheduler.
    • Blocked/Waiting: A thread can move to the Blocked/Waiting state if it is waiting for a monitor lock or waiting indefinitely with methods like Object.wait(), Thread.join(), or Lock.lock().
    • Timed Waiting: The Timed Waiting state occurs when a thread is waiting for a specified period with methods like Thread.sleep(long milliseconds), Thread.join(long milliseconds), and Object.wait(long timeout).
    • Terminated: A thread reaches the Terminated state when its run method completes or due to an unhandled exception.

    Transitions:

    • New → Runnable: When start() method is called.
    • Runnable → Blocked/Waiting: When a thread is waiting for a monitor lock or waiting indefinitely.
    • Runnable → Timed Waiting: When a thread is waiting with a timeout.
    • Blocked/Waiting → Runnable: When waiting condition is satisfied.
    • Timed Waiting → Runnable: When timeout elapses or waiting condition is satisfied.
    • Runnable → Terminated: When run method completes or a RuntimeException occurs.

3. How do you create a thread in Java?

  • Answer: You can create a thread in Java in two ways:
    1. Extending the Thread class:
      public class MyThread extends Thread {
          public void run() {
              System.out.println("Thread is running.");
          }
      }
      
      public class Main {
          public static void main(String[] args) {
              MyThread thread = new MyThread();
              thread.start();
          }
      }
      
    2. Implementing the Runnable interface:
      public class MyRunnable implements Runnable {
          public void run() {
              System.out.println("Thread is running.");
          }
      }
      
      public class Main {
          public static void main(String[] args) {
              Thread thread = new Thread(new MyRunnable());
              thread.start();
          }
      }
      

4. What is the difference between start() and run() methods in Java Thread class?

  • Answer: The start() and run() methods in the Java Thread class serve different purposes.
    • start() Method: This method is used to start the thread. It internally calls the run() method on the new thread created. Calling start() schedules the thread to be run by the thread scheduler, allowing it to perform concurrent execution.
    • run() Method: This method contains the code that is executed by the thread. When you call run() directly, it simply calls the run() method on the current thread, not in a new thread context. Consequently, the code appears to run sequentially, not concurrently.

5. Explain the concept of thread priority in Java.

  • Answer: In Java, thread priority is a hint to the thread scheduler that indicates the importance of a thread relative to other threads. The thread scheduler is free to ignore this hint, but most schedulers will give more CPU time to threads with higher priority. Java provides a range of priorities, from Thread.MIN_PRIORITY (1) to Thread.MAX_PRIORITY (10), with Thread.NORM_PRIORITY (5) being the default.

  • Setting Priority:

    Thread t = new Thread(new MyRunnable());
    t.setPriority(Thread.MAX_PRIORITY);
    
  • Getting Priority:

    int priority = t.getPriority();
    

Priority is generally used when multiple threads are in the Runnable state and need to be scheduled.

6. What is the purpose of the join() method in Java threads?

  • Answer: The join() method in Java is used to ensure that the current thread waits for the thread on which join() is called to complete its execution before moving forward. This is useful in scenarios where the main thread must wait for other threads to finish their operations before proceeding with further tasks.

  • Syntax:

    void join() throws InterruptedException
    void join(long millis) throws InterruptedException
    void join(long millis, int nanos) throws InterruptedException
    

Example:

public class Main {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            System.out.println("Thread1 is running");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Thread1 is completed");
        });

        Thread t2 = new Thread(() -> {
            System.out.println("Thread2 is running");
        });

        t1.start();
        t2.start();
        try {
            t1.join();  // Main thread waits for t1 to complete
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Main thread is completed");
    }
}

In this example, the main thread waits for t1 to finish before proceeding, ensuring Thread2 starts after Thread1 completes.

7. How does the sleep() method work in Java threads, and what is its usage?

  • Answer: The sleep() method in Java is a static method of the Thread class that causes the currently executing thread to pause its execution for a specified period of time. This is often used to control the timing of thread execution or to reduce resource usage.

  • Syntax:

    public static void sleep(long millis) throws InterruptedException
    public static void sleep(long millis, int nanos) throws InterruptedException
    

Example:

public class Main {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Iteration " + i);
                try {
                    Thread.sleep(1000);  // Sleep for 1 second
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        t.start();
    }
}

In this example, the thread prints a message every second, using sleep() to pause for 1000 milliseconds (1 second) between iterations.

8. What is a daemon thread in Java, and how do you create one?

  • Answer: A daemon thread in Java is a low-priority thread that runs in the background to perform tasks such as garbage collection. Daemon threads are typically used for services that should not prevent the JVM from exiting. When all non-daemon threads have finished executing, the JVM terminates the program, and daemon threads are stopped abruptly.

  • To create a daemon thread:

    • First, create a thread (either by extending the Thread class or implementing the Runnable interface).
    • Then, call the setDaemon(true) method on the thread before starting it.

Example:

public class Main {
    public static void main(String[] args) {
        Thread daemonThread = new Thread(() -> {
            while (true) {
                System.out.println("Daemon thread is running");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        daemonThread.setDaemon(true);
        daemonThread.start();

        // Main thread sleeps for 2 seconds and then exits
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Main thread is exiting");
    }
}

Here, the daemon thread prints a message every 500 milliseconds. When the main thread completes its sleep and exits, the daemon thread is automatically terminated.

9. Describe the interrupt() and isInterrupted() methods in Java threads.

  • Answer: The interrupt() and isInterrupted() methods are used to manage interruptions in Java threads.

  • interrupt() Method: This method is used to interrupt a thread. It sets an interruption flag for the thread, which can be checked by the thread to take appropriate action. If a thread is blocked in a waiting state (e.g., sleep(), wait(), join()), it will throw an InterruptedException.

  • isInterrupted() Method: This method tests whether the thread has been interrupted. It returns a boolean value (true if interrupted, false otherwise). The interruption flag is cleared when this method is called.

Example:

public class Main {
    public static void main(String[] args) {
        Thread worker = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("Working...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    System.out.println("Thread was interrupted");
                    Thread.currentThread().interrupt();  // Re-interrupt the thread
                }
            }
            System.out.println("Thread is exiting");
        });

        worker.start();

        try {
            Thread.sleep(3000);  // Main thread sleeps for 3 seconds
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        worker.interrupt();  // Interrupt the worker thread
    }
}

In this example, the worker thread runs a loop that checks its interruption status. If the thread is interrupted (main thread calls worker.interrupt()), it handles the InterruptedException and re-interrupts itself to ensure the loop exits and the thread terminates.

10. What are some common mistakes to avoid when working with threads in Java?

  • Answer: Working with threads can introduce various complexities and potential issues. Here are some common mistakes to avoid:
    1. Uncontrolled Thread Creation: Avoid creating too many threads, as it can lead to excessive CPU usage and resource contention.
    2. Ignoring InterruptedException: Always handle InterruptedException properly, especially when using blocking methods like sleep(), wait(), and join().
    3. Not Synchronizing Shared Resources: Ensure to synchronize access to shared resources to prevent race conditions.
    4. Deadlocks: Be cautious of deadlock situations, which occur when two threads are waiting for each other to release resources. Design thread interactions to minimize deadlock risks.
    5. Lack of Proper Thread Management: Use thread pools (e.g., ExecutorService) to manage and control thread lifecycle more efficiently.
    6. Not Using Thread Safe Collections: Use thread-safe collections from the java.util.concurrent package instead of standard collections to avoid concurrency issues.
    7. Overthrowing Thread Control: Avoid directly manipulating thread scheduling by manipulating priorities, as it can degrade performance and lead to unpredictable behavior.

By understanding and avoiding these common pitfalls, you can create more robust and efficient multithreaded Java applications.