Do you think IIT Guwahati certified course can help you in your career?
No
Introduction
Multithreading in Java is a process of executing two or more threads simultaneously. Java multithreading runs multiple applications and allocates processing power to them.
In this article, we will discuss multithreading in Java. We will discuss how to create multiple threads. Multithreading is an important feature in Java. It allows programs to utilize modern multi-core processors and perform multiple tasks concurrently, and fully. Let us understand more about multithreading in Java.
Multithreading programming in Java is a feature that allows the concurrent execution of two or more threads for maximum CPU utilization. It helps perform multiple tasks simultaneously within a single program. Each thread runs independently, sharing the same memory space, which makes Java applications more responsive and efficient, especially in real-time and complex systems.
Multithreading extends the concept of multitasking, where operations within an application are divided into individual threads. Threads in Java can be created either by extending the Thread class or by implementing the Runnable interface.
Ways to Create a Thread Class in Java
There are two mechanisms by which threads can be created:
Extending the Thread class
Extending the Thread class in Java allows you to create your custom thread by inheriting from the Thread class. This approach is used for multi-threading, where multiple threads run concurrently to perform different tasks within a program. By extending the Thread class, you have the advantage of customizing the behavior of the thread by overriding the run method. However, this approach has limitations since Java supports single inheritance. If you extend Thread, your class cannot extend to any other class.
Here's an example using the Runnable interface:
Java
Java
public class CustomRunnable implements Runnable { @Override public void run() { // Code to be executed by the custom thread } }
public class Main { public static void main(String[] args) { CustomRunnable customRunnable = new CustomRunnable(); Thread thread = new Thread(customRunnable); thread.start(); } }
You can also try this code with Online Java Compiler
Implementing the Runnable interface in Java involves creating a class that implements the Runnable interface and overriding its run method. This approach is used for creating threads and is preferred over extending the Thread class because it allows better flexibility in class design and avoids the limitations of single inheritance. By implementing the Runnable interface, you separate the task to be performed by the thread from the threading mechanism itself. This makes your code more modular and flexible.
Let's say you want to create a simple program that prints numbers from 1 to 10 using a separate thread. Here's how you could implement it using the Runnable interface:
Java
Java
public class NumberPrinter implements Runnable { @Override public void run() { for (int i = 1; i <= 10; i++) { System.out.println(i); } } }
public class Main { public static void main(String[] args) { NumberPrinter numberPrinter = new NumberPrinter(); Thread thread = new Thread(numberPrinter); thread.start(); // Start the thread to print numbers concurrently } }
You can also try this code with Online Java Compiler
In this example, the NumberPrinter class implements the Runnable interface and defines the task to print numbers. The Main class creates an instance of NumberPrinter and starts a new thread to execute the task. This allows the numbers to be printed concurrently with the main thread.
Life Cycle of a Thread in Java Multithreading
In Java multithreading, a thread goes through the following life cycle stages:
New (Created) – A thread is created using the Thread class but hasn’t started yet.
Thread t = new Thread();
Runnable – The thread is started using the start() method and is ready to run but is waiting for CPU scheduling
t.start();
Blocked – The thread is waiting for a resource (e.g., trying to enter a synchronized block but another thread holds the lock).
Waiting – The thread is indefinitely waiting for another thread to notify it via wait() or similar methods.
Timed Waiting – The thread waits for a specified time using methods like sleep(time), join(time), or wait(time).
Terminated (Dead) – The thread finishes execution or is stopped. Once terminated, it cannot be restarted.
Each of these states represents a transition in the thread's execution cycle, controlled by Java’s thread management mechanisms.
Advantages of Java Multithreading
It improves concurrency and application responsiveness. Since each activity is defined as a thread, there is no need to wait for one thread to complete to start another.
It improves program structure. Programs are written in multiple independent or semi-independent units of execution instead of a single piece of code.
Reduced utilization of resources and decreased cost of maintenance. Multiple threads share the same address and memory space or files simultaneously, so the requirement for additional resources is reduced.
Limitations of Java Multithreading
Writing code for multithreaded applications is challenging. Their results cannot be easily predicted.
Complex debugging and testing. Finding the root cause of an error and debugging it becomes complex and difficult compared to single-threaded programs. Defects related to time-slicing are often tricky to discover and test.
Inefficient management of Concurrency and Parallelism may cause problems in the application.
In Java, a thread is the course or path followed while a program is being run. Perhaps a thread is a program's thread of execution. The main thread, which is typically present in all programs, is provided by the JVM, or Java Virtual Machine, at the beginning of the program's execution. Each thread is given priority. Higher-priority threads are prioritized for execution over lower-priority threads.
Java Thread class
A thread is a program that starts with the start() method, a method() that is frequently used in this class. This function searches for the run() method, which is also a method of this class and starts running its body.
Java Thread Methods
Inthis section of the blog, we will see different methods of the Java thread class.
Method
Description
activeCount()
Returns an approximate count of active threads in the thread group and subgroups of the current thread.
checkAccess()
Determines whether the active thread is permitted to change this thread.
clone()
Because a Thread cannot be meaningfully copied, it throws the CloneNotSupportedException exception.
currentThread()
It gives a reference to the thread object that is presently in use.
dumpStack()
Prints the current thread's stack trace to the standard error stream.
enumerate(Thread[] tarray)
Copies each active thread in the thread group and any subgroups of the current thread into the provided array.
getId()
Returns the thread's identification.
getName()
Returns the name of this thread.
getPriority()
Returns the priority of this thread.
getState()
Provides the thread's state back.
Let us understand all these methods with the help of an example.
Example
class NinjasThreadClass extends Thread {
private String message;
public NinjasThreadClass(String message) {
this.message = message;
}
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(message);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
NinjasThreadClass thread = new NinjasThreadClass("Hello Ninjas from thread");
// Set the name of the thread
thread.setName("NinjasThread");
// Get the name of the thread
System.out.println("Thread name is: " + thread.getName());
// Set the priority of the thread
thread.setPriority(Thread.MAX_PRIORITY);
// Get the priority of the thread
System.out.println("Thread priority is: " + thread.getPriority());
// Check if the thread is alive
System.out.println("Thread is alive: " + thread.isAlive());
// Start the thread
thread.start();
try {
// Wait for the thread to finish
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// Check if the thread is alive after it has finished
System.out.println("Thread is alive: " + thread.isAlive());
}
}
Output
Thread name is: NinjasThread
Thread priority is: 10
Thread is alive: false
Hello Ninjas from thread
Hello Ninjas from thread
Hello Ninjas from thread
Hello Ninjas from thread
Hello Ninjas from thread
Thread is alive: false
Thread Creation
There aretwo primary ways to create threads in Java. The following are the ways:
By extending Thread class: You can create a new class that extends the Thread class and override the run() method to specify the code that the thread should execute.
By implementing the Runnable interface: You can create a new class that implements the Runnable interface and override the run() method to specify the code that the thread should execute.
What is the Thread Class?
In Java, the Thread class is a built-in class that represents a thread of execution. It provides methods to create and manage threads, as well as methods to control the execution of threads. Some important methods of the Thread class include:
start(): starts the thread by calling its run() method
run(): the method that is executed when the thread is started
sleep(): causes the thread to pause for a specified amount of time
join(): waits for the thread to complete its execution
Let us understand this with the help of an example.
class NinjasThread extends Thread {
private String message;
public NinjasThread(String message) {
this.message = message;
}
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(message);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
NinjasThread thread1 = new NinjasThread("Hello Ninjas from thread 1");
NinjasThread thread2 = new NinjasThread("Hello Ninjas from thread 2");
thread1.start();
thread2.start();
}
}
Output
Hello Ninjas from thread 1
Hello Ninjas from thread 2
Hello Ninjas from thread 1
Hello Ninjas from thread 2
Hello Ninjas from thread 1
Hello Ninjas from thread 2
Hello Ninjas from thread 1
Hello Ninjas from thread 2
Hello Ninjas from thread 1
Hello Ninjas from thread 2
What is a Runnable Interface?
In Java, the Runnable interface is a built-in interface that is used to define a task that can be executed by a thread. It contains a single method called run() that is used to specify the code that the thread should execute. To use the Runnable interface, you need to create a class that implements the Runnable interface and overrides the run() method. Let us understand this with the help of an example.
class NinjasRunnableInterface implements Runnable {
private String message;
public NinjasRunnableInterface(String message) {
this.message = message;
}
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(message);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
NinjasRunnableInterface runnable1 = new NinjasRunnableInterface("Hello Ninjas from runnable 1");
NinjasRunnableInterface runnable2 = new NinjasRunnableInterface("Hello Ninjas from runnable 2");
Thread thread1 = new Thread(runnable1);
Thread thread2 = new Thread(runnable2);
thread1.start();
thread2.start();
}
}
Output
Hello Ninjas from runnable 1
Hello Ninjas from runnable 2
Hello Ninjas from runnable 1
Hello Ninjas from runnable 2
Hello Ninjas from runnable 1
Hello Ninjas from runnable 2
Hello Ninjas from runnable 1
Hello Ninjas from runnable 2
Hello Ninjas from runnable 1
Hello Ninjas from runnable 2
Thread priority
In Multithreading, every thread has its own priority. It is defined using three constants. The priority of a thread can be set to a number between 1 to 10.
public static int NORM_PRIORITY - It gives the thread a default priority of 5.
public static int MIN_PRIORITY- It gives the thread a minimum priority of 1.
public static int MAX_PRIORITY- It gives the thread a maximum priority of 10.
Methods
getPriority() - It returns the priority of the given thread.
setPriority(int newpriority) - It changes the priority of the given thread to a new one.
IllegalArgumentException is thrown if the value of newpriority does not satisfy 1<=newpriority<=10.
import java.lang.*;
class Mthread extends Thread
{
public void run()
{
System.out.println("Thread Running");
}
}
public class Main
{
public static void main(String[] args)
{
Mthread m = new Mthread();
m.start();
m.setPriority(2);
System.out.println("Thread Priority of thread m is: " + m.getPriority());
}
}
Output
Thread Running
Thread Priority of thread m is: 2
Note:
Threads with the highest priority get the chance to execute first.
The default priority of the Main thread is 5, which can be changed.
The default priority of a thread depends on the priority of its parent thread.
Thread Scheduling
A thread schedulerin Java determines the thread for execution. A thread in the runnable state is scheduled for execution. However, if two or more threads are in the runnable state, then, based on the priority number and time of arrival, a thread is chosen, and the others are told to wait. The common thread scheduling algorithms are First Come First Serve scheduling, priority scheduling and time-slicing scheduling.
Thread Synchronization
Synchronization is a process that handles resource management. Due to concurrency, multiple threads try to access the same resource simultaneously. Synchronization uses the concept of monitoring to ensure that only one thread gets access to a resource at a time. Every thread has a monitor associated with it that can lock or unlock. Java uses synchronized blocks or methods to synchronize threads. All shared resources are put in this block.
synchronized(objectidentifier) {
// Access shared resources or code that requires sequential access
}
Thread Class vs Runnable Interface – Detailed Comparison
Feature
Thread Class
Runnable Interface
Inheritance vs Composition
Extends Thread class, meaning it cannot extend any other class.
Implements Runnable interface, allowing the class to extend other classes if needed.
Resource Sharing
Each instance of Thread creates a separate object, leading to higher memory usage.
Multiple threads can share the same Runnable instance, making it memory-efficient.
Flexibility
Less flexible since a class must extend Thread, limiting code reusability.
More flexible as a class can implement multiple interfaces while maintaining clean separation of concerns.
Frequently Asked Questions
When to use multithreading?
Use multithreading when you need to perform multiple tasks simultaneously, improve application performance, or make better use of CPU resources.
What are the two types of multithreading?
The two types of multithreading are user-level and kernel-level threads. User-level threads also known as green threads or user threads, are managed entirely by the Java Virtual Machine (JVM) and the Java application. On the other hand, kernel-level threads, also known as native threads, are managed by the operating system's kernel.
What are the three models of multithreading?
The three models of multithreading are Many-to-One, One-to-One, and Many-to-Many. These models define how user threads map to kernel threads, affecting the concurrency level, thread management efficiency, and system performance in multithreaded applications.
What is a real-life example of Java multithreading?
A real-life example of Java multithreading is a web server handling multiple client requests simultaneously. Each request runs in a separate thread, allowing the server to process many requests concurrently without blocking or delaying others.
Conclusion
Multithreading and threading concepts play an important role in Java Programming. This blog gives a brief description of the main concepts related to multithreading and Multithreading in Java. It also explains a few thread-related concepts and concludes by listing the advantages and disadvantages of Multithreading in Java.