Modern applications often perform multiple tasks simultaneously, such as processing user requests, downloading files, updating databases, or running background operations. Java supports these capabilities through multithreading, which allows multiple threads to execute concurrently within a program.
Multithreading improves application performance, responsiveness, and resource utilization. In this article, you'll learn how to create and manage threads in Java, understand the thread life cycle, and implement synchronization techniques for thread-safe programming.
Learning Objectives
Understand the concept of threads and multithreading.
Explain the Java thread life cycle.
Create threads using the Thread class.
Create threads using the Runnable interface.
Use commonly used thread methods.
Implement synchronization techniques.
Understand real-world applications of multithreading.
What is a Thread?
A thread is the smallest unit of execution within a program. Multiple threads can execute simultaneously, allowing applications to perform several tasks concurrently.
Examples of multithreading include:
Web browsers downloading files while displaying web pages.
Banking applications processing multiple transactions simultaneously.
Music applications playing songs while loading playlists.
Software applications running background tasks without interrupting users.
Java provides built-in support for multithreading through the Thread class and the Runnable interface.
What is Multithreading?
Multithreading is the process of executing multiple threads concurrently within a single application.
Benefits of Multithreading
Improved application performance
Better responsiveness
Efficient CPU utilization
Simultaneous execution of multiple tasks
Improved scalability for enterprise applications
Thread Life Cycle
A thread passes through multiple stages during its execution.
The major stages include:
New
Runnable
Running
Blocked / Waiting
Terminated
Description of Each Stage
State | Description |
New | The thread is created but not yet started. |
Runnable | The thread is ready for execution. |
Running | The thread is currently executing. |
Blocked / Waiting | The thread is temporarily paused or waiting for resources. |
Terminated | The thread has completed its execution. |
Understanding the thread life cycle helps developers manage thread execution efficiently.
Creating a Thread Using the Thread Class
One way to create a thread is by extending the Thread class.
Example
class MyThread extends Thread {
public void run() {
System.out.println(
"Thread is running.");
}
}
public class ThreadExample {
public static void main(String[] args) {
MyThread thread =
new MyThread();
thread.start();
}
}
Output
Thread is running.
The start() method creates a new thread and automatically invokes the run() method.
Note: Always use start() instead of calling run() directly when creating threads.
Creating Multiple Threads
Java allows multiple threads to execute concurrently.
Example
class FirstThread extends Thread {
public void run() {
System.out.println("First Thread");
}
}
class SecondThread extends Thread {
public void run() {
System.out.println("Second Thread");
}
}
public class Main {
public static void main(String[] args) {
FirstThread thread1 =
new FirstThread();
SecondThread thread2 =
new SecondThread();
thread1.start();
thread2.start();
}
}
The output order may vary because both threads execute independently.
Creating a Thread Using the Runnable Interface
Implementing the Runnable interface is generally considered a better practice because it allows Java programs to inherit from other classes when required.
Example
class MyRunnable implements Runnable {
public void run() {
System.out.println(
"Runnable Thread Executing");
}
}
public class RunnableExample {
public static void main(String[] args) {
Thread thread =
new Thread(new MyRunnable());
thread.start();
}
}
Output
Runnable Thread Executing
Using Runnable promotes better flexibility and code reusability.
Thread Methods
Java provides several useful methods for managing threads.
Method | Purpose |
start() | Starts thread execution |
run() | Contains thread logic |
sleep() | Pauses thread execution temporarily |
join() | Waits for another thread to complete |
isAlive() | Checks whether a thread is active |
getName() | Returns the thread name |
setName() | Changes the thread name |
currentThread() | Returns the currently executing thread |
Using sleep()
The sleep() method temporarily pauses thread execution.
Example
public class SleepExample {
public static void main(String[] args)
throws InterruptedException {
for(int i = 1; i <= 5; i++) {
System.out.println(i);
Thread.sleep(1000);
}
}
}
Output
1
2
3
4
5
The program pauses for one second between each iteration.
Using join()
The join() method waits for one thread to finish before another thread continues execution.
Example
class Task extends Thread {
public void run() {
System.out.println(
"Task Completed");
}
}
public class JoinExample {
public static void main(String[] args)
throws Exception {
Task thread =
new Task();
thread.start();
thread.join();
System.out.println(
"Main Method Completed");
}
}
Output
Task Completed
Main Method Completed
Synchronization in Java
When multiple threads access shared resources simultaneously, inconsistent results may occur. Synchronization ensures that only one thread accesses critical sections of code at a time.
Synchronization helps prevent:
Data inconsistency
Race conditions
Unexpected application behavior
Example Without Synchronization
class Counter {
int count = 0;
public void increment() {
count++;
}
}
If multiple threads update the value simultaneously, the final result may become inconsistent.
Using Synchronized Methods
Example
class Counter {
int count = 0;
public synchronized void increment() {
count++;
}
}
The synchronized keyword allows only one thread to execute the method at a time.
Synchronizing Blocks of Code
Synchronization can also be applied to specific code blocks.
Example
public void display() {
synchronized(this) {
System.out.println(
"Thread Safe Operation");
}
}
Synchronizing only the required code improves application performance.
Thread Safety
A program is considered thread-safe when shared resources are accessed correctly without causing unexpected behavior.
Thread safety becomes particularly important in:
Banking applications
Financial systems
Inventory management systems
Enterprise applications
Database-driven applications
Synchronization plays a critical role in ensuring thread safety.
Real-World Applications
Multithreading is widely used in:
Banking applications
Web servers
Gaming applications
Operating systems
File download managers
Video streaming platforms
Enterprise software systems
Database applications
Examples include:
Processing multiple customer requests simultaneously.
Running background tasks while users interact with an application.
Executing scheduled operations without interrupting system performance.
Common Mistakes
Avoid these common errors when working with threads:
Calling run() directly instead of start().
Ignoring synchronization requirements.
Creating unnecessary threads that consume system resources.
Forgetting to handle InterruptedException.
Accessing shared resources without proper synchronization.
Assuming that thread execution always follows a fixed order.
Practice Exercise
Write Java programs to:
Create a thread by extending the Thread class.
Create a thread using the Runnable interface.
Execute multiple threads simultaneously.
Pause thread execution using sleep().
Wait for thread completion using join().
Implement synchronized methods.
Demonstrate thread-safe access to shared resources.
Display thread names using getName().
Still need help?
Contact us