Object Oriented Programming in JavaUnit 108 min read
Multithreading in Java: Threads, Synchronization, and Concurrency
Unit 10 of Object Oriented Programming in Java covers multithreading concepts, thread lifecycle, inter-thread communication, synchronization, and real-world applications of concurrent programming in Java.
TAKEAWAYS:
- Understand how Java achieves multithreading via the
Threadclass andRunnableinterface. - Learn the five states of a thread lifecycle and how to control thread execution using
start(),sleep(), andjoin(). - Master synchronization mechanisms (
synchronizedkeyword,wait(),notify(),notifyAll()) to prevent race conditions. - Compare thread safety issues and solutions (e.g., deadlocks, thread pools) with practical examples.
- Apply multithreading to real-world scenarios like handling multiple user requests in eSewa or processing orders in Daraz.
Core Concepts of Multithreading
Multithreading allows a program to execute multiple threads concurrently, improving performance by utilizing CPU cores efficiently. Java supports multithreading through the Thread class and Runnable interface.
1. Thread Basics
A thread is the smallest unit of execution within a process. Java provides two ways to create threads:
- Extending
Threadclass (not recommended for complex programs due to single inheritance limitation). - Implementing
Runnableinterface (preferred, as it avoids inheritance restrictions).
Example: Creating Threads
// Using Runnable interface (recommended)
class MyRunnable implements Runnable {
public void run() {
System.out.println("Thread is running...");
}
}
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
t1.start(); // Starts the thread
}
}
Thread Lifecycle
A thread in Java goes through five states:
| State | Description |
|---|---|
| New | Thread is created but not yet started (new Thread()). |
| Runnable | Thread is ready to run (after start() is called). |
| Running | Thread is executing (run() method). |
| Blocked/Waiting | Thread waits for a monitor lock or I/O operation. |
| Terminated | Thread execution completes (run() finishes). |
In the Real World
Multithreading is widely used in Nepalese and global applications to handle concurrent tasks efficiently:
eSewa (Nepal)
- Use: Multithreading handles simultaneous user requests (e.g., bill payments, fund transfers).
- How? Each request runs in a separate thread, ensuring quick responses even during peak hours.
Daraz (Nepal/Alibaba Group)
- Use: Order processing (e.g., inventory updates, payment confirmations) runs in parallel threads.
- How? A thread checks stock availability while another processes payment, reducing delays.
WhatsApp (Meta)
- Use: Message delivery uses multithreading to send/receive messages concurrently.
- How? One thread handles incoming messages, another manages file uploads, and a third updates UI.
2. Thread Synchronization
When multiple threads access shared resources (e.g., a bank account balance), race conditions occur, leading to data corruption. Synchronization ensures thread safety.
Key Methods for Synchronization
| Method | Purpose |
|---|---|
synchronized |
Locks an object to prevent multiple threads from accessing it simultaneously. |
wait() |
Causes a thread to wait until another thread calls notify(). |
notify() |
Wakes up a single waiting thread. |
notifyAll() |
Wakes up all waiting threads. |
Example: Bank Account Transfer (Thread-Safe)
class BankAccount {
private int balance = 1000;
public synchronized void withdraw(int amount) {
if (balance >= amount) {
System.out.println("Withdrawing: " + amount);
balance -= amount;
System.out.println("New balance: " + balance);
}
}
}
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount();
Thread t1 = new Thread(() -> account.withdraw(300));
Thread t2 = new Thread(() -> account.withdraw(400));
t1.start();
t2.start();
}
}
Output (Thread-Safe):
Withdrawing: 300
New balance: 700
Withdrawing: 400
New balance: 300
3. Inter-Thread Communication
Threads often need to coordinate (e.g., producer-consumer problem). Java provides:
wait(): Puts a thread in waiting state.notify(): Wakes up one waiting thread.notifyAll(): Wakes up all waiting threads.
Example: Producer-Consumer Problem
class SharedBuffer {
private int item;
private boolean empty = true;
```figure
{"type":"network","nodes":["Producer","Buffer","Consumer"],"edges":[["Producer","Buffer","produce()"],["Buffer","Consumer","consume()"],["Buffer","Producer","wait()"],["Buffer","Consumer","notify()"]],"directed":true,"caption":"Producer-consumer interaction with wait/notify"}
public synchronized void produce(int value) {
while (!empty) wait(); // Wait if buffer is full
item = value;
empty = false;
System.out.println("Produced: " + item);
notify(); // Notify consumer
}
public synchronized void consume() {
while (empty) wait(); // Wait if buffer is empty
System.out.println("Consumed: " + item);
empty = true;
notify(); // Notify producer
}
}
public class Main { public static void main(String[] args) { SharedBuffer buffer = new SharedBuffer(); Thread producer = new Thread(() -> buffer.produce(100)); Thread consumer = new Thread(() -> buffer.consume()); producer.start(); consumer.start(); } }
**Output:**
Produced: 100 Consumed: 100
4. Thread Pools (Executor Framework)
Creating threads manually is inefficient. Java’s Executor Framework (ExecutorService) manages thread pools for better performance.
Example: Using ExecutorService
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3); // 3 threads
for (int i = 0; i < 5; i++) {
Runnable task = () -> System.out.println("Task " + i + " executed by " + Thread.currentThread().getName());
executor.execute(task);
}
executor.shutdown();
}
}
Output (varies based on thread scheduling):
Task 0 executed by pool-1-thread-1
Task 1 executed by pool-1-thread-2
Task 2 executed by pool-1-thread-3
Task 3 executed by pool-1-thread-1
Task 4 executed by pool-1-thread-2
5. Common Threading Issues & Solutions
| Issue | Cause | Solution |
|---|---|---|
| Race Condition | Multiple threads access shared data unsafely. | Use synchronized blocks. |
| Deadlock | Threads wait indefinitely for locks. | Avoid nested locks; use timeouts. |
| Starvation | Some threads never get CPU time. | Use fair scheduling (new FairBlockingQueue). |
| Thread Leak | Threads are not terminated properly. | Always call shutdown() on ExecutorService. |
Exam Tip
- Define multithreading clearly (execution of two or more threads concurrently).
- Differentiate
Threadvs.Runnable: PreferRunnablefor flexibility. - Explain synchronization with a bank account example (race condition →
synchronized). - Draw thread lifecycle diagrams (New → Runnable → Running → Terminated).
- Code a producer-consumer problem using
wait()andnotify(). - Mention thread pools (
ExecutorService) for efficiency in real-world apps.
Visual Summary:
Based on the TU BCA syllabus for Object Oriented Programming in Java (CACS204), unit 10.
Discussion
Loading…