In today's fast-paced digital world, where users expect lightning-fast responses and seamless experiences, the performance and responsiveness of applications are more important than ever. Whether you're building a simple desktop app, a mobile game, or a large-scale web platform, users won't tolerate laggy or unresponsive behavior. To meet these expectations, developers must design systems that can handle multiple tasks efficiently and simultaneously.
One of the most effective techniques to boost application performance is multitasking, the ability to run more than one task at a time. For instance, think about your daily use of a computer or smartphone: you might be watching a video, downloading files in the background, typing notes, and receiving real-time notifications, all at once. This is multitasking in action.
In the world of Java programming, multitasking is commonly achieved using threads. Threads allow different parts of a program to execute concurrently, leading to better CPU utilization and faster application response times. Threads are particularly useful in scenarios where a program needs to perform background tasks, like handling user inputs, performing file operations, or making network requests, without freezing the main user interface.
Understanding how multitasking and multithreading work in Java is essential for any developer aiming to build high-performance, scalable applications. Whether you are developing standalone software, interactive games, or enterprise-level web applications, leveraging Java’s multithreading capabilities can significantly enhance your application’s efficiency and user experience.
In this blog, we'll break down the concept of multitasking in Java into simple terms. We'll cover its types, how threads work, how to create them, and the key differences between threads and processes. Along the way, we'll also look at practical examples and code snippets to help you understand each concept clearly.
Let’s get started and explore how Java enables multitasking through threads, step by step.
What is Single Tasking?
Single tasking means the system can perform only one task at a time. The processor stays idle during input/output operations, leading to poor utilization.
Example
DOS (Disk Operating System)
Only one program can run at a time.
What is Multi-tasking?
Multitasking allows the processor to handle multiple tasks at the same time, improving efficiency and performance by reducing idle time.
Example
Windows OS, where a user can:
Listen to music
Download a file
Write a document
Chat with a friend at the same time
Types of Multitasking in Java
There are two ways to achieve multitasking:
1. Processor-based Multitasking
Executing multiple independent tasks.
Each task has its own memory and resources.
Handled by the operating system.
Example:
Running a Java program
Playing music
Downloading files
Copying software
Browsing websites
2. Thread-based Multitasking
Executing multiple parts of the same program.
Tasks may share memory and resources.
Managed by the programming language, like Java.
Example:
Games and web applications where animations, sound, and user input run in parallel.
Difference Between Thread and Process
Aspect | Process | Thread |
Definition | Independent program in execution | Sub-part (lightweight) of a process |
Memory | Has its own separate memory | Shares memory with other threads of the same process |
Creation Speed | Slower and uses more resources | Faster and uses fewer resources |
Communication | Complex (via IPC mechanisms) | Easy (uses shared memory) |
Crash Impact | One process crash doesn't affect others | One thread crash can affect the entire process |
What is a Thread in Java?
A thread is a lightweight sub-process that allows multitasking within a single program.
Main Thread:
Every Java application has a default thread called the main thread, which starts from the main()
method.
How to Get Information About the Current Thread in Java
In Java, every application starts with a default thread known as the main thread. This thread is created by the Java Virtual Machine (JVM) and is responsible for executing the main()
method of your class.
To retrieve information about the currently running thread, Java provides a method called
Thread.currentThread()
. This method returns a reference to the thread that is currently executing the code.
public class MasteringBackend { public static void main(String[] args) { // Get the reference of the currently running thread Thread currentThread = Thread.currentThread(); // Display the thread information System.out.println("Current Thread Information:"); System.out.println("Thread Name : " + currentThread.getName()); System.out.println("Thread ID : " + currentThread.getId()); System.out.println("Thread Priority : " + currentThread.getPriority()); System.out.println("Thread State : " + currentThread.getState()); System.out.println("Thread Group : " + currentThread.getThreadGroup().getName()); } }
In this example, we use the Thread
class in Java, which provides detailed information about the thread. Since the main thread is executing, the output will appear as follows:
Output
Current Thread Information: Thread Name : main Thread ID : 1 Thread Priority : 5 Thread State : RUNNABLE Thread Group : main
Key Methods from Thread
Class:
getName()
Returns the name of the thread.
Default value is
"main".
getId()
Returns a unique identifier for the thread.
getPriority()
Returns the thread’s priority.
Default is
5
(Thread.NORM_PRIORITY).
getState()
Returns the current state of the thread.
Possible states:
NEW,
RUNNABLE,
WAITING, TIMED_WAITING,
BLOCKED,
TERMINATED.
getThreadGroup()
Returns the thread group the thread belongs to.
Typically
"main"
for the main thread.
Life Cycle of a Thread
A thread goes through different phases:
New: Thread object is created.
Runnable:
start()
is called, ready to run.Running: Chosen by the thread scheduler to run.
Waiting: Waiting for another thread.
Terminated: Task is completed.
Thread Priority in Java
In Java, each thread has a priority, which is an integer value between 1 (MIN_PRIORITY) and 10 (MAX_PRIORITY). The default priority is 5, which is represented by Thread.NORM_PRIORITY.
Thread priority is used as a hint to the thread scheduler to determine the order in which threads are scheduled for execution. However, Java does not guarantee that a higher-priority thread will always run before a lower-priority one. It depends on the thread scheduler implementation, which is platform-dependent.
Assigning priorities can be useful in scenarios where certain tasks are more critical and should be given preference over others.
Example
public class MasteringBackend { public static void main(String[] args) { // Create three threads using anonymous classes Thread lowPriorityThread = new Thread(() -> { System.out.println("Low Priority Thread is running..."); }); Thread normalPriorityThread = new Thread(() -> { System.out.println("Normal Priority Thread is running..."); }); Thread highPriorityThread = new Thread(() -> { System.out.println("High Priority Thread is running..."); }); // Set priorities lowPriorityThread.setPriority(Thread.MIN_PRIORITY); // Priority = 1 normalPriorityThread.setPriority(Thread.NORM_PRIORITY); // Priority = 5 highPriorityThread.setPriority(Thread.MAX_PRIORITY); // Priority = 10 // Print the priorities for understanding System.out.println("Low Priority Thread Priority : " + lowPriorityThread.getPriority()); System.out.println("Normal Priority Thread Priority : " + normalPriorityThread.getPriority()); System.out.println("High Priority Thread Priority : " + highPriorityThread.getPriority()); // Start all threads lowPriorityThread.start(); normalPriorityThread.start(); highPriorityThread.start(); } }
Output
Low Priority Thread Priority : 1 Normal Priority Thread Priority : 5 High Priority Thread Priority : 10 High Priority Thread is running... Normal Priority Thread is running... Low Priority Thread is running...
Creating Threads in Java
In Java, multithreading is achieved by creating and running multiple threads. A thread in Java can be created using either:
Extending the
Thread
classImplementing the
Runnable
interface
Both approaches require overriding the run()
method, which contains the code that will be executed by the thread.
1 Extending the Thread Class
This is the simplest way to create a thread. Here, you create a new class that extends the Thread
class and override its run()
method.
Steps:
Create a class that extends
Thread.
Override the
run()
method to define the thread’s task.Create an object of the class.
Call the
start()
method to begin thread execution.
Syntax:
class MyThread extends Thread { public void run() { // Code to execute in a new thread } }
Example
class MyThread extends Thread { public void run() { System.out.println("Thread is running using Thread class..."); } } public class MasteringBackend { public static void main(String[] args) { MyThread mt = new MyThread(); // Step 3: Create thread object mt.start(); // Step 4: Start the thread (calls run internally) } }
Output
Thread is running using Thread class..
2. Implementing the Runnable Interface
This approach is preferred when you want your class to extend another class (since Java doesn't support multiple inheritance with classes). You implement the Runnable
interface and pass the instance to a Thread
object.
Steps:
Create a class that implements
Runnable.
Override the
run()
method.Create an object of that class.
Create a
Thread
object and pass theRunnable
instance to it.Call
start()
on theThread
object
Syntax:
class MyRunnable implements Runnable { public void run() { // Code to execute in a new thread } }
Example
class MyRunnable implements Runnable { public void run() { System.out.println("Thread is running using Runnable interface..."); } } public class MasteringBackend { public static void main(String[] args) { MyRunnable mr = new MyRunnable(); // Step 3 Thread t = new Thread(mr); // Step 4 t.start(); // Step 5 } }
Output
Thread is running using Runnable interface...