Threads
Multithreading is a powerful concept in Java that allows us to run multiple threads
concurrently within a single process. It’s crucial for developing responsive and
efficient applications, especially in today’s multi-core processor environments.
What is Multithreading?
Multithreading is a programming concept that allows a single process to execute
multiple threads concurrently. Threads are lightweight sub-processes within a
process that share the same memory space, but they can run independently. Each
thread represents a separate flow of control, making it possible to perform multiple
tasks simultaneously within a single program.
Threads are smaller units of a process, sharing the same memory space.
Threads can be thought of as independent, parallel execution paths.
Multithreading enables efficient utilization of multi-core processors.
• Threads are smaller units of a process, sharing the same memory space.
• Threads can be thought of as independent, parallel execution paths.
• Multithreading enables efficient utilization of multi-core processors.
•Improved Responsiveness: Multithreading allows applications to remain
responsive to user input, even when performing resource-intensive tasks. For
example, a text editor can continue responding to user actions while performing a
spell-check in the background.
• Enhanced Performance: Multithreaded programs can take advantage of multi-core
processors, leading to better performance. Tasks can be divided among multiple
threads, speeding up computation.
• Resource Sharing: Threads can share data and resources within the same process,
which can lead to more efficient memory usage. This can be crucial in memory-
intensive applications.
• Concurrency: Multithreading enables concurrent execution of tasks, making it
easier to manage multiple tasks simultaneously. For instance, a web server can
handle multiple client requests concurrently using threads.
Terminology and Concepts
To understand multithreading, it’s essential to grasp the following key concepts:
Thread: A thread is the smallest unit of execution within a process. Multiple threads
can exist within a single process and share the same memory space.
Process: A process is an independent program that runs in its memory space. It can
consist of one or multiple threads.
Concurrency: Concurrency refers to the execution of multiple threads in overlapping
time intervals. It allows tasks to appear as if they are executing simultaneously.
Parallelism: Parallelism involves the actual simultaneous execution of multiple
threads or processes, typically on multi-core processors. It achieves true
simultaneous execution.
Race Condition: A race condition occurs when two or more threads access shared
data concurrently, and the final outcome depends on the timing and order of
execution. It can lead to unpredictable behavior and bugs.
Synchronization: Synchronization is a mechanism used to coordinate and control
access to shared resources. It prevents race conditions by allowing only one thread
to access a resource at a time.
Deadlock: Deadlock is a situation in which two or more threads are unable to
proceed because each is waiting for the other to release a resource. It can result in a
system freeze.
Real-life Example of Java Multithreading
Suppose you are using two tasks at a time on the computer, be it using Microsoft
Word and listening to music. These two tasks are called processes. So you start
typing in Word and at the same time start music app, this is called multitasking. Now
you committed a mistake in a Word and spell check shows exception, this means
Word is a process that is broken down into sub-processes. Now if a machine is dual-
core then one process or task is been handled by one core and music is been handled
by another core.
In the above example, we come across both multiprocessing and multithreading.
These are somehow indirectly used to achieve multitasking. In this way the
mechanism of dividing the tasks is called multithreading in which every process or
task is called by a thread where a thread is responsible for when to execute, when to
stop and how long to be in a waiting state. Hence, a thread is the smallest unit of
processing whereas multitasking is a process of executing multiple tasks at a time.
The Thread Lifecycle:
Threads in Java go through various states in their lifecycle:
New: When a thread is created but not yet started.
Runnable: The thread is ready to run and is waiting for its turn to execute.
Running: The thread is actively executing its code.
Blocked/Waiting: The thread is temporarily inactive, often due to waiting for a
resource or event.
Terminated: The thread has completed execution and has been terminated.
Two Ways to Implement Multithreading
Using Thread Class
Using Runnable Interface
Method 1: Using Thread Class
Java provides Thread class to achieve programs involving threads. Some major
methods of thread class are shown.
Methods Action Performed
isDaemon() It checks whether the current thread is
daemon or not
start() It starts the execution of the thread
run() It executes the statements in the body of
this method over a thread
sleep() It is a static method that puts the thread
to sleep for a certain time been passed
as an argument to it
wait() It sets the thread back in waiting state.
notify() It gives out a notification to one thread
that is in waiting state
notifyAll() It gives out a notification to all the
thread in the waiting state
setDaemon() It set the current thread as Daemon
thread
stop() It is used to stop the execution of the
thread
resume() It is used to resume the suspended
thread
By default, threads are named thread-0, thread-1, and so on. But there is also a
method that is often used as setName() method. Also corresponding to it there is a
method getName() which returns the name of the thread be it default or settled
already by using setName() method. The syntax is as follows:
Syntax:
(a) Returning the name of the thread
public String getName() ;
(b) Changing the name of the thread
public void setName(String name);
Consider if one has to multiply all elements by 2 and there are 500 elements in an
array.
public class FirstThread extends Thread {
// run() method is called as
// soon as the thread is started
public void run()
// Print statement when the thread is called
System.out.println("First Thread is running now");
public class SecondThread extends Thread {
public void run()
System.out.println("Second Thread is running now");
public class ThreadDemo1 {
public static void main(String[] args)
// Creating a thread object of our thread class
FirstThread firstThread = new FirstThread();
SecondThread secondThread = new SecondThread();
// Getting the threads to the runnable state
// This thread will transcend from runnable to run
// as start() method will look for run() and execute
// it
firstThread.start();
// This thread will also transcend from runnable to
// run as start() method will look for run() and
// execute it
secondThread.start();
When you run the program above multiple times, the output will vary, should you
are expecting the first thread to run before the second as it was started first. Now
this is the reason:
The thread scheduler — part of your JVM and operating system — decides when
each thread gets CPU time.
It considers many factors like:
• Current CPU load
• Priority of threads
• Number of processor cores
• Internal JVM/OS scheduling strategies
Because of this, there is no guarantee that the first thread you start will be the first
to execute.
The out vary because when you start firstThread:
The JVM asks the OS to run it. The OS might immediately run it or put it in a
queue and run something else first.
When you then start secondThread, it also gets submitted to the OS scheduler.
The OS might decide: “Run secondThread now, firstThread later” (leading to
Second Thread is running now first) Or “Run firstThread now, secondThread
later” (leading to the opposite order).
Since the scheduling decision can be different each time — due to timing
differences, CPU state, and other processes running — your output can vary
between runs.
public class ThirdThread extends Thread{
public void run() {
// Print statement when the thread is called
System.out.println(" Third Thread 1 is running");
public class FourthThread extends Thread{
// Method
public void show() {
// Print statement when thread is called
System.out.println("Fourth Thread is running");
}
public class ThreadDemo2 {
public static void main(String[] args) {
// Creating a thread object of our thread class
ThirdThread thirdThread = new ThirdThread();
FourthThread fourthThread = new FourthThread();
thirdThread.start();
// This thread will now look for run() method which is absent
// Thread is simply created not runnable
fourthThread.start();
Method 2 – By Implementing the Runnable Interface
public class FifthThread implements Runnable{
public void run() {
// Iterating to get more execution of threads
for (int i = 0; i < 5; i++) {
System.out.println("Runnable Thread1");
try {
Thread.sleep(1000);
// Catch block to handle the exceptions
catch (Exception e) {
public class SixthThread implements Runnable{
public void run()
// Iterating to get more execution of threads
for (int i = 0; i < 5; i++) {
System.out.println("Runnable Thread2");
try {
Thread.sleep(1000);
catch (Exception e) {
public class ThreadDemo3 {
public static void main(String[] args)
// Creating reference of Runnable to
// the classes implementing runable
Runnable rO1 = new FifthThread();
Runnable rO2 = new SixthThread();
// Creating reference of thread class
// by passing object of Runnable in constructor of
// Thread class
Thread t1 = new Thread(rO1);
Thread t2 = new Thread(rO2);
t1.start();
t2.start();
Serial Execution using sleep
public class SerialSleep1 extends Thread{
public void show()
// Iterating to print more number of times
for (int i = 0; i < 5; i++) {
// Print statement
System.out.println("Shot");
// Making thread to sleep using sleep() method
// Try-catch block for exceptions
try {
Thread.sleep(1000);
catch (Exception e) {
public class SerialSleep2 extends Thread {
public void show()
// Iterating to print more number of times
for (int i = 0; i < 5; i++) {
// Print statement
System.out.println("Miss");
// Making thread to sleep using sleep() method
// Try-catch block for exceptions
try {
Thread.sleep(1000);
catch (Exception e) {
}
}
public class SerialDemo {
public static void main(String[] args) {
// Creating objects in the main() method
SerialSleep1 serialSleep1 = new SerialSleep1();
SerialSleep2 serialSleep2 = new SerialSleep2();
// Starting the thread objects
serialSleep1.start();
serialSleep2.start();
// Calling methods of class 1 and class 2
serialSleep1.show();
serialSleep2.show();
Parallel Execution Using Sleep
public class ParallelSleep1 extends Thread{
public void run()
for (int i = 0; i < 5; i++) {
System.out.println("Shot");
// Making thread to sleep using sleep() method
// Try catch block for exceptions
try {
Thread.sleep(1000);
catch (Exception e) {
public class ParallelSleep2 extends Thread{
public void run()
for (int i = 0; i < 5; i++) {
// Print statement
System.out.println("Miss");
// Making thread to sleep using sleep() method
// Try catch block for exceptions
try {
Thread.sleep(1000);
catch (Exception e) {
}
public class ParallelSleepDemo {
public static void main(String[] args)
// Creating objects in the main() method
ParallelSleep1 parallelSleep1 = new ParallelSleep1();
ParallelSleep2 parallelSleep2 = new ParallelSleep2();
// Starting the thread objects
// using start() method
// start() method calls the run() method
// automatically
parallelSleep1.start();
parallelSleep2.start();
Thread Priority
Notice that so far, there is no priority been set for threads for which as per the order
of execution of threads outputs will vary so do remember this drawback of
multithreading of different outputs leading to data inconsistency issues which we
will be discussing in-depth in the later part under synchronization in threads.
Priorities in Threads
Priorities in threads is a concept where each thread is having a priority which is
represented by numbers ranging from 1 to 10.
The default priority is set to 5 as excepted.
Minimum priority is set to 1.
Maximum priority is set to 10.
Here 3 constants are defined in it namely as follows:
public static int NORM_PRIORITY
public static int MIN_PRIORITY
public static int MAX_PRIORITY
No Priority
public class NoPriority extends Thread{
public void run()
System.out.println("Running Thread : " + currentThread().getName());
System.out.println("Running Thread Priority : " + currentThread().getPriority());
public class NoPriorityDemo {
public static void main(String[] args)
// Creating objects of MyThread(above class)
// in the main() method
NoPriority np1 = new NoPriority();
NoPriority np2 = new NoPriority();
// Case 1: Default Priority no setting
np1.start();
np2.start();
Norm Priority
public class NormPriority extends Thread {
public void run()
System.out.println("Running Thread : " + currentThread().getName());
System.out.println("Running Thread Priority : " + currentThread().getPriority());
public class NormPriorityDemo {
public static void main(String[] args)
// Creating objects of MyThread(above class)
// in the main() method
NormPriority np1 = new NormPriority();
NormPriority np2 = new NormPriority();
// Setting priority to thread via NORM_PRIORITY
// which set priority to 5 as default thread
np1.setPriority(Thread.NORM_PRIORITY);
np2.setPriority(Thread.NORM_PRIORITY);
// Setting default priority using
// NORM_PRIORITY
np1.start();
np2.start();
Min Priority
public class MinPriority extends Thread{
public void run()
// Printing the current running thread via getName()
// method using currentThread() method
System.out.println("Running Thread : "
+ currentThread().getName());
// Print and display the priority of current thread
// via currentThread() using getPriority() method
System.out.println("Running Thread Priority : "
+ currentThread().getPriority());
}
public class MinPriorityDemo {
public static void main(String[] args)
// Creating objects of MyThread(above class)
// in the main() method
MinPriority mp1 = new MinPriority();
MinPriority mp2 = new MinPriority();
// Setting priority to thread via NORM_PRIORITY
// which set priority to 1 as least priority thread
mp1.setPriority(Thread.MIN_PRIORITY);
mp2.setPriority(Thread.MIN_PRIORITY);
// Setting default priority using
// NORM_PRIORITY
mp1.start();
mp2.start();
Max Priority
public class MaxPriority extends Thread{
public void run()
{
// Printing the current running thread via getName()
// method using currentThread() method
System.out.println("Running Thread : "
+ currentThread().getName());
// Print and display the priority of current thread
// via currentThread() using getPriority() method
System.out.println("Running Thread Priority : "
+ currentThread().getPriority());
public class MaxPriorityDemo {
public static void main(String[] args)
// Creating objects of MyThread(above class)
// in the main() method
MaxPriority mp1 = new MaxPriority();
MaxPriority mp2 = new MaxPriority();
// Setting priority to thread via MAX_PRIORITY
// which set priority to 10 as most priority thread
mp1.setPriority(Thread.MAX_PRIORITY);
mp2.setPriority(Thread.MAX_PRIORITY);
// Setting default priority using
// MAX_PRIORITY
// Starting the threads using start() method
// which automatically invokes run() method
mp1.start();
mp2.start();
Daemon Threads
In Java, daemon threads are special types of threads that run in the background and
are typically used for tasks such as garbage collection, background maintenance, or
handling other non-essential tasks in an application. They are marked as daemon
because they do not prevent the JVM from exiting when the program finishes
executing.
Key Points about Daemon Threads:
Background Execution:
Daemon threads run in the background and do not have an essential role in the
execution of the main program. For example, the garbage collector is a daemon
thread.
Program Exit:
When all non-daemon (also called user) threads in a Java application finish
execution, the JVM will exit, even if daemon threads are still running. This means
that daemon threads do not prevent the JVM from terminating.
Creation:
You can create a daemon thread by calling the setDaemon(true) method before
starting the thread:
Priority:
Daemon threads have a lower priority compared to user threads. They exist to
provide services to user threads but will not prevent an application from exiting.
Use Cases:
Daemon threads are often used for low-priority background tasks, like monitoring
system resources, log writing, mail delivery failure (Mailer Daemons) or garbage
collection.
public class DaemonThread1 extends Thread{
public void run() {
// Checking whether the thread is daemon thread or
// not
if (Thread.currentThread().isDaemon()) {
// Print statement when Daemon thread is called
System.out.println( "I am daemon thread and I am working");
else {
// Print statement whenever users thread is
// called
System.out.println( "I am user thread and I am working");
}
public class DaemonThread1Demo {
public static void main(String[] args)
// Creating threads in the main body
DaemonThread1 dt1 = new DaemonThread1();
DaemonThread1 dt2 = new DaemonThread1();
DaemonThread1 dt3 = new DaemonThread1();
// Setting thread named 't2' as our Daemon thread
dt2.setDaemon(true);
// Starting all 3 threads using start() method
dt1.start();
dt2.start();
dt3.start();
// Now start() will automatically
// invoke run() method
public class DaemonThread2 extends Thread{
public void run()
// Checking whether the thread is daemon thread or
// not
if (Thread.currentThread().isDaemon()) {
// Print statement when Daemon thread is called
System.out.println( "I am daemon thread and I am working");
else {
// Print statement whenever users thread is
// called
System.out.println( "I am user thread and I am working");
public class DaemonThread2Demo {
public static void main(String[] args)
// Creating threads objects of above class
// in the main body
DaemonThread2 dt1 = new DaemonThread2();
DaemonThread2 dt2 = new DaemonThread2();
DaemonThread2 dt3 = new DaemonThread2();
// Starting all 3 threads using start() method
dt1.start();
dt2.start();
dt3.start();
// Now start() will automatically invoke run()
// method
// Now at last setting already running thread 't2'
// as our Daemon thread will throw an exception
dt2.setDaemon(true);