0
Explore
0

Thread and Thread class in Java – A Detailed Explanation

Updated on August 1, 2025

In Java, threading is one of the most powerful features that allows us to execute multiple tasks simultaneously. It plays a critical role in modern software development especially in scenarios where we need concurrent execution, such as downloading a file while playing music, or loading a UI screen while fetching data in the background.

Think of a thread as a small unit of a process. A process can have multiple threads running inside it, all doing different things at the same time.

What is a Thread?

A Thread in Java is a lightweight subprocess. It’s the smallest unit of execution within a program. Java allows us to execute multiple threads concurrently using its multithreading feature.

Example in Real Life:

Imagine a restaurant:

  • One thread is taking orders from customers.
  • Another thread is cooking the food.
  • A third thread is serving the food.

All these actions happen simultaneously, not one after another. That’s multithreading in action!

Why Use Threads?

  • To make efficient use of CPU resources.
  • To perform multiple tasks concurrently.
  • To keep applications responsive (especially GUIs).
  • To handle asynchronous tasks like file I/O, network requests, or animations.

How to Create a Thread in Java?

Java provides two main ways to create a thread:

By Extending the Thread Class

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

    public static void main(String[] args) {
        MyThread t1 = new MyThread();  // creating thread
        t1.start();                    // starting thread
    }
}

Explanation:

  • We extend the Thread class.
  • Override the run() method (which contains the thread’s code).
  • Call start() to begin the thread’s execution (it internally calls run()).

By Implementing the Runnable Interface

class MyRunnable implements Runnable {
    public void run() {
        System.out.println("Runnable thread is running...");
    }

    public static void main(String[] args) {
        MyRunnable obj = new MyRunnable();
        Thread t1 = new Thread(obj); // Passing the Runnable object
        t1.start();
    }
}

Explanation:

  • We implement the Runnable interface and override run().
  • Create a Thread object by passing our Runnable object.
  • Call start() on the Thread object.

Difference Between Thread vs Runnable

FeatureThread ClassRunnable Interface
InheritanceCannot extend other classCan extend another class
ReusabilityLess reusableMore reusable
Memory consumptionSlightly moreSlightly less
FlexibilityLess flexibleMore flexible

Life Cycle of a Thread

A thread in Java goes through the following states:

  • New: The thread is created but not started yet using the start() method.
  • Runnable: The thread is ready to run and waiting for CPU time. It may be scheduled for execution at any time.
  • Running: The thread is currently executing its run() method.
  • Blocked: The thread is waiting to acquire a monitor lock, usually because another thread is executing a synchronized block/method it needs.
  • Waiting: The thread is waiting indefinitely for another thread to perform a specific action (e.g., using wait()).
  • Timed Waiting: The thread is waiting for a specified amount of time (e.g., using sleep(ms), join(timeout), or wait(timeout)).
  • Terminated (Dead): The thread has finished execution or was forcefully stopped.

Thread Lifecycle Diagram

  New
   |
 start()
   ↓
Runnable → Running → (Waiting / Timed Waiting / Blocked)
   ↓                      ↓
Terminated ←-------------- 

What is the Thread Class in Java?

In Java, the Thread class is a built-in class in the java.lang package that allows you to create and manage threads. It provides methods and mechanisms to perform multithreading — i.e., running two or more parts of a program concurrently.

Key Points:

  • It is used to create and control threads in a Java application.
  • You can create a thread by either:
    • Extending the Thread class, or
    • Implementing the Runnable interface and passing it to a Thread object.
  • The Thread class implements the Runnable interface.

Thread Class Syntax:

public class Thread extends Object implements Runnable

Commonly Used Constructors:

ConstructorDescription
Thread()Creates a new thread with no target
Thread(Runnable target)Creates a thread with a Runnable object
Thread(String name)Creates a thread and assigns a name
Thread(Runnable target, String name)Runnable with custom name

Commonly Used Methods:

MethodDescription
start()Starts the thread and calls the run() method
run()Code inside this method is executed by the thread
sleep(milliseconds)Pauses the thread for a specific time
join()Waits for the thread to finish
isAlive()Checks if the thread is still running
getName()Returns the thread’s name
setName(String)Sets a name for the thread
getPriority()Gets thread’s priority (1 to 10)
setPriority(int)Sets thread’s priority
currentThread()Returns a reference to the currently executing thread

Thread VS Thread class

  • Thread refers to the individual unit of execution, and its life cycle refers to the various states it goes through from creation to termination.
  • The Thread class is just a blueprint used to create a thread. So we usually describe the life cycle of the actual thread, not the class.

Example: Creating Two Threads

Below program creates two threads in different ways:

  1. One by extending the Thread class
  2. One by implementing the Runnable interface

Both threads perform a simple task: they print messages (“Hello – i” and “World – i”) 5 times each.

class HelloThread extends Thread {
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("Hello - " + i);
        }
    }
}

class WorldRunnable implements Runnable {
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("World - " + i);
        }
    }
}

public class MultiThreadExample {
    public static void main(String[] args) {
        HelloThread t1 = new HelloThread();
        Thread t2 = new Thread(new WorldRunnable());

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

Output

World - 1
World - 2
Hello - 1
Hello - 2
World - 3
World - 4
Hello - 3
Hello - 4
World - 5
Hello - 5

Code Explanation

Class 1: HelloThread extends Thread

class HelloThread extends Thread {
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("Hello - " + i);
        }
    }
}
  • This class extends the Thread class.
  • The run() method is overridden to define the code that will run in the new thread.
  • Inside the loop, it prints: Hello – 1, Hello – 2, …, Hello – 5.

Class 2: WorldRunnable implements Runnable

class WorldRunnable implements Runnable {
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("World - " + i);
        }
    }
}
  • This class implements the Runnable interface.
  • It also overrides the run() method.
  • It prints: World – 1, World – 2, …, World – 5.

Main Class: MultiThreadExample

public class MultiThreadExample {
    public static void main(String[] args) {
        HelloThread t1 = new HelloThread(); // Thread created using Thread class
        Thread t2 = new Thread(new WorldRunnable()); // Thread created using Runnable interface

        t1.start(); // Starts HelloThread
        t2.start(); // Starts WorldRunnable as a thread
    }
}
  • t1 is a thread object created by directly extending Thread.
  • t2 is a thread object created by passing a Runnable object into the Thread constructor.
  • Both threads are started using .start().

What Happens When the Program Runs

When the main() method runs:

  • t1.start() and t2.start() begin execution in parallel (concurrently).
  • The order of output is unpredictable because both threads are executing independently.

Possible Output:

Hello - 1
World - 1
Hello - 2
World - 2
...

Or maybe:

World - 1
World - 2
World - 3
Hello - 1
Hello - 2
...

👉 The exact output may change every time you run the program due to how threads are scheduled by the CPU.

Summary: Key Concepts

ConceptExplanation
Thread classUsed to create a thread by extending it and overriding run()
Runnable interfaceUsed to create a thread by implementing Runnable and passing to Thread constructor
start() methodStarts a new thread and calls run() internally
run() methodContains the code that runs in the new thread
ConcurrencyBoth threads run independently; output order is not guaranteed

Thread Priorities

  • In Java, each thread has a priority, which is an integer value between 1 and 10.
  • Thread priority tells the Thread Scheduler how important a thread is.
  • However, thread priority is a suggestion, not a guarantee — it may or may not be respected depending on the JVM and OS.

Priority Constants in Java (from Thread class)

ConstantValueDescription
Thread.MIN_PRIORITY1Lowest priority
Thread.NORM_PRIORITY5Default priority
Thread.MAX_PRIORITY10Highest priority

Why Use Thread Priority?

When multiple threads are ready to run, the thread scheduler can choose the thread with the highest priority first. But again, it’s not guaranteed — it’s OS and JVM dependent.

Thread priorities help the Thread Scheduler decide which thread to execute first (not guaranteed).

Java Program: Thread Priorities Example

class PriorityThread extends Thread {
    public PriorityThread(String name) {
        super(name);
    }

    public void run() {
        for (int i = 1; i <= 3; i++) {
            System.out.println(getName() + " is running with priority " + getPriority() + " - Count: " + i);
        }
    }
}

public class ThreadPriorityDemo {
    public static void main(String[] args) {
        PriorityThread t1 = new PriorityThread("LowPriorityThread");
        PriorityThread t2 = new PriorityThread("NormalPriorityThread");
        PriorityThread t3 = new PriorityThread("HighPriorityThread");

        // Set priorities
        t1.setPriority(Thread.MIN_PRIORITY);   // 1
        t2.setPriority(Thread.NORM_PRIORITY);  // 5 (default)
        t3.setPriority(Thread.MAX_PRIORITY);   // 10

        // Start threads
        t1.start();
        t2.start();
        t3.start();
    }
}

Output

HighPriorityThread is running with priority 10 - Count: 1
HighPriorityThread is running with priority 10 - Count: 2
LowPriorityThread is running with priority 1 - Count: 1
NormalPriorityThread is running with priority 5 - Count: 1
...

Important Note: The output order is not guaranteed even with set priorities. Some JVMs may not honor them strictly — especially on OSs like Windows.

Explanation

  1. We define a PriorityThread class extending Thread, which prints its name, priority, and count.
  2. We create three threads:
    • LowPriorityThread with priority 1
    • NormalPriorityThread with priority 5
    • HighPriorityThread with priority 10
  3. We start all three threads.

Key Points to Remember

  • Priority can influence execution when the CPU is deciding between multiple ready threads.
  • Not deterministic: High priority thread may not always execute first.
  • Use setPriority(int) to assign a priority between 1 and 10.
  • Use getPriority() to check a thread’s priority.

Thread.sleep() in Java

  • Thread.sleep(milliseconds) is used to pause the execution of the current thread for a specific amount of time.
  • This means the thread will go to sleep and not do anything during that time.
  • After the sleep time finishes, the thread resumes execution.

Why use Thread.sleep()?

  • To simulate delays (like in countdowns, timers, or waiting for another task).
  • To reduce CPU usage for background or less critical tasks.
  • To control the execution flow of threads, especially when working with multiple threads.

Java Program 1: Thread Sleep Example

public class SleepExample {
    public static void main(String[] args) {
        for (int i = 1; i <= 3; i++) {
            System.out.println("Count: " + i);
            try {
                Thread.sleep(1000); // sleep for 1 second (1000 ms)
            } catch (InterruptedException e) {
                System.out.println(e);
            }
        }
    }
}

Output:

Count: 1
(wait 1 second)
Count: 2
(wait 1 second)
Count: 3

Step-by-step Explanation

Line-by-line:

  1. for (int i = 1; i <= 3; i++)
    ➤ A loop that runs 3 times (prints count 1 to 3).
  2. System.out.println("Count: " + i);
    ➤ Prints the current count on each loop iteration.
  3. Thread.sleep(1000);
    ➤ Pauses the current thread for 1000 milliseconds (or 1 second) before continuing.
  4. try-catch block
    ➤ Java requires you to handle InterruptedException when using sleep().
    ➤ This exception may occur if another thread interrupts the sleeping thread.

Java Program 2: Simulating a Countdown Timer

public class CountdownTimer {
    public static void main(String[] args) {
        System.out.println("Countdown starting...");

        for (int i = 5; i >= 1; i--) {
            System.out.println("T-minus " + i + " seconds");
            try {
                Thread.sleep(1000); // pause for 1 second
            } catch (InterruptedException e) {
                System.out.println("Timer interrupted!");
            }
        }

        System.out.println("Blast off! 🚀");
    }
}

Output

Countdown starting...
T-minus 5 seconds
T-minus 4 seconds
T-minus 3 seconds
T-minus 2 seconds
T-minus 1 seconds
Blast off! 🚀

This is a simple countdown timer where Thread.sleep() creates a real-time delay, just like a real countdown!

Thread Synchronization (Short Explanations)

When multiple threads access shared data at the same time, we can face problems like data inconsistency. To handle this, we use synchronization.

synchronized void printTable(int n) {
    for (int i = 1; i <= 5; i++) {
        System.out.println(n * i);
    }
}

The synchronized keyword locks the method/object to prevent race conditions.

Multithreading vs Multiprocessing

FeatureMultithreadingMultiprocessing
DefinitionMultiple threads in one processMultiple processes
Resource UsageLowHigh
CommunicationEasier (shared memory)Harder (separate memory)
SpeedFaster context switchingSlower

Best Practices

  • Avoid using Thread.stop() (it’s deprecated).
  • Always handle InterruptedException.
  • Prefer ExecutorService for thread pool management (advanced).
  • Avoid shared mutable state; use synchronization carefully.
  • Use thread-safe collections like ConcurrentHashMap.

Conclusion

Threads in Java allow you to build responsive, high-performance, and concurrent applications. While it might seem tricky at first, understanding the basic structure, lifecycle, and synchronization techniques will help you write effective multithreaded programs.

If you’re building applications that need to do many things at once, mastering threads is a must-have skill.