Java Programming Thread Lifecycle And Thread Class Complete Guide

 Last Update:2025-06-22T00:00:00     .NET School AI Teacher - SELECT ANY TEXT TO EXPLANATION.    9 mins read      Difficulty-Level: beginner

Understanding the Core Concepts of Java Programming Thread Lifecycle and Thread Class

Java Programming: Thread Lifecycle and Thread Class

  1. New State: When a thread object is created using the new keyword but has not yet started running, it is in the new state.

    Thread t = new Thread();
    
  2. Runnable State: A thread moves into the runnable state after calling the start() method on it. In this state, the thread is ready to run and waits for the thread scheduler to allocate CPU time.

    Thread t = new Thread();
    t.start(); // now thread moves into runnable state
    
  3. Blocked State: If the thread is trying to enter a synchronized block or method but cannot do so because another thread holds the lock, it enters the blocked state.

    synchronized (lock) {
        // critical section code
    }
    
  4. Waiting State: A thread enters the waiting state when it expects to be notified by another thread to proceed. Methods like wait(), join(), and sleep() can lead to this state.

    obj.wait(); // causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
    
  5. Timed Waiting State: Similar to the waiting state, but the thread will stay in this state for a certain period or until notified. Methods that can cause a thread to be in timed waiting are sleep(long millis), wait(long timeout), join(long millis), etc.

    Thread.sleep(2000); // causes the current thread to pause execution for 2000 milliseconds
    
  6. Terminated State: This is the final state of a thread's life. Once a thread completes its execution or returns from run() method, it is in the terminated state.

    public void run() {
        // thread logic here
        System.out.println("Thread execution completed.");
        // once all code in run() is executed, thread becomes terminated
    }
    

Thread Class in Java The Thread class in Java is a fundamental part of concurrency as it provides methods to control the execution of threads and manage their states.

  1. Constructors:

    • Thread(): Creates a new thread with default priority and no specified name.
    • Thread(Runnable r): Creates a new thread where the run() method is delegated to the Runnable parameter.
    • Thread(Runnable r, String name): Same as above but also sets the thread name.
    • Thread(String name): Creates a new thread with a specific name.
  2. Common Methods:

    • void start(): Starts the thread in a separate call stack; internally calls the run() method.
    • void run(): Contains the code to be executed by the thread.
    • static void sleep(long millis): Causes the current thread to pause execution for a specified number of milliseconds. Can throw InterruptedException.
    • void join(): Makes the calling thread wait for the thread represented by this to complete.
    • void interrupt(): Interrupts the thread on which it is called.
    • boolean isInterrupted(): Tests whether the thread has been interrupted.
    • static boolean interrupted(): Returns a boolean indicating the interrupted status of the thread.
    • boolean isAlive(): Returns a boolean indicating if the thread represented by this is still alive.
    • int getPriority(): Returns the priority of this thread.
    • void setPriority(int pri): Changes the priority of the thread.
    • String getName(): Returns the name of the thread.
    • void setName(String Name): Changes the name of the thread.
    • Thread.State getState(): Returns the current state of the thread.
  3. Synchronization Related Methods:

    • void wait(): Causes the current thread to wait until another thread calls the notify() or notifyAll() method for this thread’s monitor object.
    • void notify(): Wakes up a single thread that is waiting on this thread's monitor object.
    • void notifyAll(): Wakes up all the threads that are waiting on this thread's monitor object.
  4. Thread Priority Levels:

    • MIN_PRIORITY: The minimum priority value assigned to a thread.
    • NORM_PRIORITY: Default priority assigned to a thread.
    • MAX_PRIORITY: Maximum priority a thread can have.

    Setting thread priority can be done using setPriority():

    Thread t = new Thread();
    t.setPriority(Thread.MAX_PRIORITY);
    
  5. Daemon Threads: Daemon threads are service provider threads that run in the background and provide services to the user threads. They have a lower priority than user threads. JVM terminates all daemon threads when all user threads are dead.

    Thread t = new Thread();
    t.setDaemon(true);
    
  6. Thread Groups: Threads can belong to thread groups which make it easier to control the multiple threads at once.

    ThreadGroup group = new ThreadGroup("MyGroup");
    Thread t = new Thread(group, myRunnable, "thread-1", stackSize);
    

Important Concepts

  • Thread Context Switching: The process where the CPU changes the context of one thread to another. This involves saving the state of current thread and loading the saved state of another thread.

  • Deadlock: Situation in multithreading where two or more threads are blocked forever, each waiting on the other.

  • Liveness Issues: These are problems related to the performance and responsiveness of applications. Deadlocks, race conditions, starvevation, and livelocks fall under this category.

  • Race Conditions: Occurs when two or more threads can access shared data and they try to change it at the same time. As a result, behavior of the program is unpredictable.

  • Thread Interference: Happens when two threads perform operations on the shared resource concurrently.

  • Thread Safety: Code or objects that operate correctly when used concurrently.

  • Atomic Operations: Operations that cannot be interrupted or subdivided into smaller steps once they have begun execution. Java provides AtomicInteger, AtomicLong, etc. classes to handle such operations safely.

  • Volatile Keyword: Indicates that a variable may be changed unpredictably by other parts of your program, usually by another thread. Variables declared using volatile are visible to all threads.

    Example of volatile usage:

    public class Counter {
        private volatile int count = 0;
    
        public int getCount() {
            return count;
        }
    
        public void increment() {
            count++;
        }
    }
    
  • Synchronized Blocks and Methods: These are blocks of code marked with a synchronized keyword that allow only one thread at a time to execute within them.

    Synchronized method example:

    public synchronized void incrementCount() {
        count++;
    }
    

    Synchronized block example:

    public void incrementCount() {
        synchronized(this) { 
            count++;
        } 
    }
    
  • Executor Framework: A high-level threading framework providing tools for managing and controlling threads, thread pools.

    Example of Executor Service:

    ExecutorService executor = Executors.newFixedThreadPool(10);
    executor.execute(new Runnable() {
        public void run() {
            // task code here
        }
    });
    
  • ThreadLocal Variables: These variables differ from their normal counterparts in that each thread that accesses one will have its own, independently initialized copy of the variable.

Online Code run

🔔 Note: Select your programming language to check or run code at

💻 Run Code Compiler

Step-by-Step Guide: How to Implement Java Programming Thread Lifecycle and Thread Class

Step 1: Introduction to Threads

A thread in Java is a sequence of instructions within a program that can be executed concurrently with other threads. Java has a built-in Thread class which you can use or extend to create your own threads.

Step 2: Creating Threads

You can create threads in Java using two methods:

  • Extending the Thread class: This involves creating a subclass of Thread and overriding its run() method.
  • Implementing the Runnable interface: This involves creating a class that implements Runnable and overrides its run() method.

Example 1: Extending the Thread class

public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread is running.");
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start();
    }
}

In this example:

  • MyThread extends the Thread class and overrides the run() method.
  • In the Main class, we create an instance of MyThread and call its start() method to begin execution.

Example 2: Implementing the Runnable interface

public class MyRunnable implements Runnable {
    @Override
    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();
    }
}

In this example:

  • MyRunnable implements the Runnable interface and overrides the run() method.
  • We create a Thread object, passing it an instance of MyRunnable. Then call the start() method on the Thread object to start the execution.

Step 3: Thread Lifecycle

Threads in Java go through several states during their lifetime:

  • New: A thread is in this state when it is created but not yet started.
  • Runnable: A thread is in this state as soon as you call the start() method on it. It might be running or waiting for CPU cycles to execute.
  • Blocked: The thread is in this state if it tries to enter a synchronized block/method but other threads are already inside it.
  • Waiting: The thread enters this state by calling methods like wait(), join(), or sleep(long) with a non-zero argument.
  • Timed Waiting: The thread enters this state by calling methods like sleep(long), join(long), wait(long).
  • Terminated: The thread is in this state after the run() method has completed.

Let’s take a look at some examples demonstrating different states.

Example 3: Using sleep()

public class SleepExample extends Thread {
    public void run() {
        try {
            System.out.println("Thread is going to sleep for 5000 ms");
            Thread.sleep(5000);
            System.out.println("Thread is awake");
        } catch (InterruptedException e) {
            System.out.println(e.toString());
        }
    }

    public static void main(String[] args) {
        SleepExample t = new SleepExample();
        System.out.println("State before starting thread: " + t.getState()); //NEW
        t.start();
        System.out.println("State after starting thread: " + t.getState());  //RUNNABLE
        while (t.isAlive()) {
            System.out.println("Thread is still running"); //TIMED_WAITING
            try {
                Thread.sleep(1000);    // Sleep for 1 sec
            } catch (InterruptedException e) {
                System.out.println(e.toString());
            }
        }
        System.out.println("State when thread is completed: " + t.getState()); //TERMINATED
    }
}

Explanation:

  • Before starting the thread, it's in the NEW state.
  • After calling start(), it transitions into the RUNNABLE state.
  • When the thread calls Thread.sleep(), it enters the TIMED_WAITING state.
  • After waking up from sleep and completing the run() method, the thread is in the TERMINATED state.

Step 4: Joining Threads

The join() method allows one thread to wait for the completion of another thread.

Example 4: Using join()

public class JoinExample extends Thread {
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is running");
    }

    public static void main(String[] args) {
        Thread t1 = new JoinExample();
        Thread t2 = new JoinExample();

        t1.start();
        try {
            // Main thread will wait for t1 to complete
            t1.join();
        } catch (InterruptedException e) {
            System.out.println(e.toString());
        }

        t2.start();
    }
}

Explanation:

  • t1 starts first and runs its run() method.
  • The main thread waits for t1 to complete using t1.join().
  • Only after t1 finishes, t2 is allowed to start.

Step 5: Synchronizing Threads

Sometimes, you need to synchronize threads to avoid race conditions. One way to do this in Java is by using the synchronized keyword.

Example 5: Synchronized Threads

public class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

public class SyncExample extends Thread {
    private Counter counter;

    public SyncExample(Counter counter) {
        this.counter = counter;
    }

    public void run() {
        for (int i = 0; i < 1000; i++) {
            counter.increment();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();

        SyncExample t1 = new SyncExample(counter);
        SyncExample t2 = new SyncExample(counter);

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("Count: " + counter.getCount());
    }
}

Explanation:

  • Counter.increment() is synchronized so that only one thread can access it at a time.
  • Both t1 and t2 increment the counter 1000 times.
  • The main thread waits for both t1 and t2 to complete by calling join() on them.
  • Without synchronization, the final count might be less than 2000 due to race conditions. With synchronization, it ensures that the final count is exactly 2000.

Summary

  • New: Thread is created but hasn't been started.
  • Runnable: Thread has started and can be scheduled for execution by the JVM.
  • Blocked/Waiting/Timed Waiting: States where a thread is not allowed to execute.
  • Terminated: After the run() method completes.

Additional Tips

  • Use Thread.sleep(ms) method to pause the current thread for the specified milliseconds.
  • Use Thread.join() to make the main thread wait until another thread completes its execution.
  • Use synchronized blocks/methods to control access to critical sections of code.

Top 10 Interview Questions & Answers on Java Programming Thread Lifecycle and Thread Class

1. What are the states of a thread in Java?

  • Answer: In Java, a thread's lifecycle consists of the following states:
    • New: A thread is in this state when an object of type Thread is created but the start() method is not yet called on that thread.
    • Runnable: Once the start() method is called on the thread object, it moves to this state. While in this state, the thread may be running (executing its run() method) or waiting for CPU allocation (ready to run).
    • Blocked: A thread is in this state when it is waiting to acquire a lock on an object to enter a synchronized block or method.
    • Waiting: This state is reached when a thread calls methods like Object.wait(), Thread.join(), or LockSupport.park(), causing it to sleep indefinitely until another thread wakes it up.
    • Timed Waiting: This state is reached when a thread calls methods like Object.wait(long timeout), Thread.join(long millis), Thread.sleep(long millis), or LockSupport.parkNanos(long nanos) for a specified time.
    • Terminated: A thread is in this state when the run() method completes (finishes execution).

2. What is the purpose of the Thread class in Java?

  • Answer: The Thread class in Java is used to create and manage threads. It encapsulates thread behaviors, such as priority, name, and its state. The Thread class contains methods to control thread execution, like start(), stop(), join(), sleep(), etc. Extending the Thread class allows you to define a new thread's behavior by overriding the run() method.

3. What is the difference between start() and run() methods of Thread class?

  • Answer: The start() and run() methods are both used in the Thread class, but they serve different purposes:
    • start(): This method is called to request the JVM to schedule the thread for execution. Internally, it calls the run() method of the thread (or the Runnable's run() method, if the thread was created by implementing Runnable). Once start() is called, the thread moves to the Runnable state and the JVM invokes the run() method on the thread.
    • run(): This method contains code that defines the task of the thread. If you override this method, you must explicitly call super.run() to ensure that the thread executes properly, unless you intend to replace the entire behavior of the thread with your custom implementation.

4. Can a thread be started multiple times in Java?

  • Answer: No, a thread can be started only once. Invoking the start() method on a thread that is already in the Runnable state or has already terminated will throw an IllegalThreadStateException. Once a thread finishes its execution, it cannot be started again; instead, a new thread instance must be created and started.

5. How can one stop a thread in Java?

  • Answer: There are a few ways to stop a thread in Java, but it’s generally discouraged to abruptly terminate threads due to potential resource leakage and inconsistent state. Recommended practices are:
    • Using a Flag (Volatility): Set a boolean flag (often volatile) that the thread checks periodically to decide whether to terminate.
    • Interruption: The interrupt() method sets the thread’s interrupt status. Threads can then check this status and exit gracefully.
    • Avoiding Thread.stop(): The stop() method is deprecated and shouldn’t be used because it can cause inconsistent states and locks can’t be released safely.

6. What does the join() method do in Java?

  • Answer: The join() method is used to wait for a thread to terminate. There are three overloaded forms of join():
    • join(): Waits indefinitely for the thread to die.
    • join(long mills): Waits at most mills milliseconds for the thread to die.
    • join(long mills, int nanos): Waits at most mills milliseconds plus nanos nanoseconds for the thread to die.
    • This is useful when a thread needs to wait for one or more other threads to finish before it continues its execution.

7. Explain how to set and get the thread priority in Java?

  • Answer: In Java, thread priority can be set and checked using the setPriority() and getPriority() methods respectively:
    • Set Priority: You can set the priority of a thread using setPriority(int newPriority), where newPriority can range from Thread.MIN_PRIORITY (1) to Thread.MAX_PRIORITY (10). Thread.NORM_PRIORITY is the default priority (5).
    • Get Priority: The getPriority() method returns the thread’s current priority.
    • It’s important to note that thread priority is only a hint to the thread scheduler, and it is up to the JVM to determine scheduling.

8. What is the difference between daemon and user threads in Java?

  • Answer: In Java, threads can be classified into two types: daemon and user (non-daemon) threads.
    • User Threads: These are threads that are typically created and controlled by the program. The JVM waits for all non-daemon threads to terminate before itself shutting down.
    • Daemon Threads: These are service-provider threads that run in the background to perform tasks such as garbage collection. Daemon threads don’t prevent the JVM from exiting if all user threads finish. If all remaining threads are daemon threads, the JVM exits.
    • You can set a thread as a daemon using setDaemon(true) before starting the thread. Daemon threads do not inherit daemon status from parent threads.

9. How to handle multiple threads synchronization in Java?

  • Answer: Handling synchronization in Java involves ensuring that multiple threads can safely work on shared data without causing inconsistent states or race conditions. Common techniques include:
    • Synchronized Methods: Declaring a method synchronized ensures that only one thread can execute the method at a time on a given instance.
    • Synchronized Blocks: If only a part of the method needs to be synchronized, use synchronized blocks.
    • Lock Interface (java.util.concurrent.locks.Lock): Provides more flexible locking mechanisms than synchronized methods, such as tryLock(), which allows thread to check if a lock is available without waiting.
    • ReentrantLock: A reentrant mutual exclusion Lock with the same basic behavior and semantics as the implicit monitor lock accessed using synchronized methods and statements. It differs from synchronized in that it is more flexible; a thread can hold a lock any number of times.
    • Volatile Keyword: This keyword ensures that changes made by one thread to a volatile variable are visible to all other threads.

10. What is the meaning of thread deadlock in Java, and how can it be avoided?

  • Answer: Thread deadlock is a situation in multi-threaded programming where two or more threads are blocked forever because each is waiting for the other to release a resource. Deadlocks happen when the following four conditions (known as the necessary conditions for deadlock) are met:
    • Mutual Exclusion: Only one thread can occupy a resource at a time.
    • Hold and Wait: A thread holding at least one resource is waiting to acquire additional resources held by other threads.
    • No Preemption: Resources cannot be forcibly taken from a thread, they must be released voluntarily.
    • Circular Wait: A circular wait involves two or more threads waiting on each other in a circular chain, where each thread is waiting for another to release a resource.

Avoiding Deadlock: Some strategies to prevent deadlock are: - Breaking the Circular Wait condition by assigning a global order to resources, and requiring threads to obtain resources only in increasing order. - Breaking the Hold and Wait condition by not allowing threads to hold on to multiple resources at once; they must request all required resources at once. - Breaking the No Preemption condition by forcibly removing resources from held threads. - Breaking the Mutual Exclusion condition by using non-exclusive locks wherever possible.

You May Like This Related .NET Topic

Login to post a comment.