Multithreading in Java is a programming concept that allows a single program to execute multiple threads concurrently. Each thread represents an independent path of execution, enabling tasks such as file downloads, data processing, and user interface updates to run simultaneously. It can be a handy skill for Java developers.
Are you one of them? Then, this blog is created for individuals like you to help you understand what multithreading is in Java. It will discuss the meaning, the lifecycle, and different ways to create a thread in Java.
'What is Multithreading in Java?' is obviously the first question in our readers' minds. Here is what you need to know: Java Multithreading allows a program to execute multiple threads at the same time. It lets tasks run in parallel and makes better use of the CPU. A thread is a lightweight and independent unit of execution within a program. Here are two things that you must keep in mind. The first one is that a program can have multiple threads. Another one is that each thread operates independently and all share the same memory space.
Let me give you a real-life example to help with your understanding. Imagine a coffee shop where multiple baristas (Threads) are preparing different kinds of coffees at the same time. It speeds up the process and utilizes all available resources (CPU).

Read Also: What is Java Used For? Key Applications and Benefits Explained
The lifecycle of a thread in Java Multithreading travels through a number of stages. It starts with the formation of the thread and ends with its finishing. Here is a diagram to display the complete lifecycle and the given steps to understand it better-

You can follow either of the two mechanisms to create threads, which I have discussed below with examples-
The first step is defining the class that extends Thread and overrides its run () method to specify the task. The next step is to create an instance of this class and invoke start (). This would trigger the run () method and initiate the thread's execution. You can extend the thread class if your class does not extend any other class.
Coffee Shop (Extending Thread)
class BrewingTask extends Thread { private String task; BrewingTask(String task) { this.task = task; } public void run() { System.out.println(task + " is being prepared by " + Thread.currentThread().getName()); } } public class Coffee { public static void main(String[] args) { Thread t1 = new BrewingTask("Espresso"); Thread t2 = new BrewingTask("Mocha"); Thread t3 = new BrewingTask("Americano"); Thread t4 = new BrewingTask("Latte"); t1.start(); t2.start(); t3.start(); t4.start(); } } |
This is what the output would look like-
Espresso is being prepared by Thread-1 Mocha is being prepared by Thread-3 Americano is being prepared by Thread-2 Latte is being prepared by Thread-0 |
Here is a little explanation for you-
We create a class that implements the java.lang.Runnable interface and define its run() method. The next step is to create a Thread object passing the Runnable instance and call start () on the Thread object to begin execution. You can go for implementing the runnable surface if the class already extends another one.
Coffee Shop (Runnable Interface)
class BrewingJob implements Runnable { private String task; BrewingJob(String task) { this.task = task; } public void run() { System.out.println(task + " is being prepared by " + Thread.currentThread().getName()); } } public class BrewingRunnable { public static void main(String[] args) { Thread t1 = new Thread(new BrewingJob("Latte")); Thread t2 = new Thread(new BrewingJob("Mocha")); Thread t3 = new Thread(new BrewingJob("Espresso")); t1.start(); t2.start(); t3.start(); } } |
This is what the output would look like-
Latte is being prepared by Thread-2 Mocha is being prepared by Thread-1 Espresso is being prepared by Thread-0 |
Here is a little explanation for you-
Implement the runnable surface to enable a class to run as a thread. It has the following three steps-
The first step is to implement the run() method from the Runnable interface. This serves as the thread's entry point and your complete business logic should go inside it.
| public void run( ) |
Follow the given constructor to instantiate a thread object in the second step-
| Thread(Runnable threadObj, String threadName); |
Invoke the start () method after creating a thread object to launch it. This kicks off the run () method in a new thread of execution.
| void start(); |
Read Also: Top 70 Java MCQs With Answers
The second way is to create a thread by defining a class that extends Java's Thread class. This approach is done in two ways and gives the benefit of direct access to thread-specific methods like start (), sleep () or interrupt (). This gives you more control over thread behaviour.
You need to override the run () method from the thread class. This method serves as the thread's starting point and should contain all your business logic.
class RunnableDemo implements Runnable { private Thread t; private String threadName;
RunnableDemo( String name) { threadName = name; System.out.println("Creating " + threadName ); }
public void run() { System.out.println("Running " + threadName ); try { for(int i = 4; i > 0; i--) { System.out.println("Thread: " + threadName + ", " + i); // Let the thread sleep for a while. Thread.sleep(50); } } catch (InterruptedException e) { System.out.println("Thread " + threadName + " interrupted."); } System.out.println("Thread " + threadName + " exiting."); }
public void start () { System.out.println("Starting " + threadName ); if (t == null) { t = new Thread (this, threadName); t.start (); } } } public class TestThread { public static void main(String args[]) { RunnableDemo R1 = new RunnableDemo( "Thread-1"); R1.start();
RunnableDemo R2 = new RunnableDemo( "Thread-2"); R2.start(); } } |
Invoke the start () method after creating a thread object to begin execution. This will trigger the JVM to run its run () method in a new thread.
| void start( ); |
class ThreadDemo extends Thread { private Thread t; private String threadName;
ThreadDemo( String name) { threadName = name; System.out.println("Creating " + threadName ); }
public void run() { System.out.println("Running " + threadName ); try { for(int i = 4; i > 0; i--) { System.out.println("Thread: " + threadName + ", " + i); // Let the thread sleep for a while. Thread.sleep(50); } } catch (InterruptedException e) { System.out.println("Thread " + threadName + " interrupted."); } System.out.println("Thread " + threadName + " exiting."); }
public void start () { System.out.println("Starting " + threadName ); if (t == null) { t = new Thread (this, threadName); t.start (); } } } public class TestThread { public static void main(String args[]) { ThreadDemo T1 = new ThreadDemo( "Thread-1"); T1.start();
ThreadDemo T2 = new ThreadDemo( "Thread-2"); T2.start(); } } |
Multithreading in Java is more than just a programming technique. It's a gateway to building high-performance, responsive, and scalable applications. By allowing multiple threads to run concurrently, Java enables developers to fully utilize multi-core processors, leading to improved application performance and responsiveness.
In Java, the start() method is used to initiate a new thread of execution, while the run() method contains the code that constitutes the new thread's task. Calling start() invokes the run() method in a new thread; however, directly invoking run() does not start a new thread but executes the run() method in the current thread. Therefore, to achieve multithreading, it's essential to call start() rather than run() directly.
A process is an independent program that runs in its own memory space, while a thread is a lightweight subprocess within a process. Threads within the same process share the same memory space, allowing for efficient communication and resource sharing. This distinction is crucial for understanding how multithreading optimizes performance by enabling concurrent execution within a single application.
Multithreading in Java is used to run multiple tasks at the same time, improving performance, responsiveness, and efficient use of CPU resources.
It improves performance, allows concurrent execution, and enhances responsiveness in applications.
You can implement it by extending the Thread class or implementing the Runnable interface.
Synchronization prevents multiple threads from accessing shared data at the same time, avoiding errors.