1.
Introduction to Multithreading
What is a Thread?
A Thread is the smallest unit of execution inside a process.
A process can contain one or more threads.
Each thread performs a separate task.
Definition
A thread is an independent path of execution within a process.
Real-Life Example
Imagine a smartphone.
You are
• Listening to music
• Downloading a file
• Browsing the internet
• Receiving WhatsApp messages
All these tasks happen simultaneously.
Each task is executed by a different thread.
Java Example
Every Java program starts with one thread called the Main Thread.
public class Main {
public static void main(String[] args) {
[Link]("Hello");
Even though we didn't create any thread,
Java automatically creates
Main Thread
What is Multithreading?
Definition
Multithreading is the process of executing multiple threads simultaneously within a single process.
Simple Definition
Running multiple tasks at the same time inside one program.
Example
Suppose you're using YouTube.
Different threads perform:
• Playing video
• Loading comments
• Downloading data
• Updating progress bar
All happen together.
Why Do We Need Multithreading?
Without Multithreading
Task 1
Task 2
Task 3
Tasks execute one after another.
The application becomes slow.
With Multithreading
Task 1
Task 2
Task 3
All execute simultaneously.
The application becomes responsive.
Advantages of Multithreading
• Faster execution
• Better CPU utilization
• Efficient memory usage
• Better user experience
• Background task execution
• Simultaneous task processing
Ways to Create Threads
Java provides two methods.
1. Extending Thread class
2. Implementing Runnable interface
Method 1: Thread Class
What is Thread Class?
Thread is a predefined Java class.
It already contains everything required to create a thread.
Package
[Link]
Steps
Step 1
Extend Thread class
class MyThread extends Thread
Step 2
Override run()
public void run()
Step 3
Create object
Step 4
Call start()
Example
class MyThread extends Thread {
public void run() {
[Link]("Thread is running");
public class Main {
public static void main(String[] args) {
MyThread t = new MyThread();
[Link]();
Output
Thread is running
How does start() work?
When we call
[Link]();
JVM
Creates new thread
Calls run()
Executes code
Why don't we call run() directly?
Wrong
[Link]();
This is just a normal method call.
No new thread is created.
Correct
[Link]();
Creates a new thread.
Difference Between start() and run()
start() run()
Creates new thread Doesn't create thread
JVM calls run() Normal method call
Concurrent execution Sequential execution
Used to start thread Contains thread code
Example
class Demo extends Thread {
public void run() {
[Link]("Inside Thread");
public class Main {
public static void main(String[] args) {
Demo d = new Demo();
[Link]();
[Link]("Main Thread");
Possible Output
Main Thread
Inside Thread
OR
Inside Thread
Main Thread
Output order is unpredictable because thread scheduling is handled by the JVM and Operating
System.
Method 2: Runnable Interface
Why Runnable?
Java supports Single Inheritance.
Suppose
class Student extends Person
Student already extends Person.
Now Student cannot extend Thread.
Solution
Implement Runnable.
Steps
Step 1
implements Runnable
Step 2
Override run()
Step 3
Pass Runnable object to Thread
Step 4
Call start()
Example
class Demo implements Runnable {
public void run() {
[Link]("Runnable Thread");
public class Main {
public static void main(String[] args) {
Demo d = new Demo();
Thread t = new Thread(d);
[Link]();
Output
Runnable Thread
Thread Class vs Runnable Interface
Thread Class Runnable Interface
Extends Thread Implements Runnable
Cannot extend another class Can extend another class
Less flexible More flexible
Recommended only for simple cases Recommended for most applications
Thread Life Cycle
A thread passes through different states.
New
Runnable
Running
Waiting / Blocked
Runnable
Terminated
1. New
Object created.
Thread t = new Thread();
2. Runnable
After
[Link]();
Thread waits for CPU.
3. Running
CPU starts executing
run()
4. Waiting
Thread waits because of
sleep()
join()
wait()
5. Terminated
Thread finishes execution.
Important Thread Methods
start()
Starts new thread.
[Link]();
run()
Contains thread code.
public void run()
sleep()
Pauses thread.
[Link](1000);
1000 milliseconds = 1 second.
Example
for(int i=1;i<=5;i++){
[Link](i);
[Link](1000);
join()
Waits until another thread completes.
[Link]();
Example
Thread finishes
Main continues
currentThread()
Returns current thread.
[Link]();
Example
[Link]([Link]().getName());
Output
main
getName()
Returns thread name.
[Link]();
Output
Thread-0
setName()
Changes thread name.
[Link]("Download Thread");
Synchronization
What is Synchronization?
Synchronization ensures that only one thread can access a shared resource at a time, preventing
inconsistent or incorrect results.
Why Synchronization?
Suppose two threads update the same bank account balance simultaneously.
Without synchronization, both may read the same old value and produce an incorrect final balance.
This problem is called a Race Condition.
Race Condition Example
Balance = ₹1000
Thread A withdraws ₹500.
Thread B withdraws ₹500.
Without synchronization,
Both threads may read ₹1000 before either updates it.
Possible incorrect balance:
₹500
or another inconsistent value instead of the expected ₹0.
synchronized Method
class Counter {
int count = 0;
synchronized void increment() {
count++;
Only one thread can execute increment() at a time.
synchronized Block
Instead of locking the whole method,
lock only required code.
synchronized(this){
count++;
Improves performance.
Executor Framework
Creating many threads manually is inefficient.
Java provides the Executor Framework to manage threads.
What is Executor Framework?
The Executor Framework manages a pool of reusable threads and assigns tasks to them
automatically.
Instead of creating a new thread for every task, existing threads are reused.
Advantages
• Better performance
• Thread reuse
• Easy task management
• Efficient CPU utilization
• Scalable applications
Main Classes
Executor
ExecutorService
Executors
Example
ExecutorService executor = [Link](2);
[Link](new Task());
[Link]();
Only two worker threads are created.
Many tasks can be executed using these reusable threads.
Thread Class vs Executor Framework
Thread Executor Framework
Creates threads manually Manages thread pool automatically
Suitable for few threads Suitable for many tasks
Less efficient More efficient
Harder to manage Easier to manage
1. Thread Class
Problem:
Create a class NumberThread that extends the Thread class. Override the run() method to print the
numbers from 1 to 10. Create an object of the class and start the thread using the start() method.
2. Runnable Interface
Problem:
Create a class MessageTask that implements the Runnable interface. The run() method should print
the message "Learning Java Multithreading" five times. Create a Thread object using the Runnable
object and execute it.
3. Thread Methods (sleep(), join(), getName(), setName(), currentThread())
Problem:
Create a thread that prints the numbers from 1 to 5 with a 1-second delay between each number
using [Link](). Rename the thread to "Counter Thread" using setName() and display its name
using getName(). In the main() method, use join() so that the main thread waits for the child thread
to finish before printing "Execution Completed".
4. Synchronization
Problem:
Create a Counter class with a variable count initialized to 0. Create two threads that each increment
the counter 1000 times. Use the synchronized keyword to ensure the final value of count is 2000.
5. Executor Framework (Introduction)
Problem:
Create a class Task that implements the Runnable interface and prints the name of the current
thread. Execute five tasks using an ExecutorService with a fixed thread pool of size 2, and properly
shut down the executor after all tasks are submitted.