Advanced Java ProgrammingUnit 510 min read
Multithreading & Concurrency: Threads, Synchronization, Deadlocks & RMI
Unit 5 of Advanced Java Programming covers multithreading concepts, thread lifecycle, synchronization mechanisms, race conditions, deadlocks, and Java’s concurrency utilities (e.g., ExecutorService, Callable, Future). It also introduces Remote Method Invocation (RMI) for distributed multithreading, with real-world exam
1. Introduction to Multithreading
Why Multithreading?
Multithreading allows a program to execute multiple threads concurrently within a single process, improving performance by utilizing CPU cores efficiently. Threads share the same memory space, reducing overhead compared to separate processes.
Thread vs. Process
| Feature | Thread | Process |
|---|---|---|
| Definition | Lightweight unit of execution | Heavyweight unit of execution |
| Memory | Shares memory with parent | Has its own memory space |
| Creation | Faster (lower overhead) | Slower (higher overhead) |
| Communication | Easier (shared memory) | Requires IPC (e.g., pipes) |
| Isolation | Less secure (shared resources) | More secure (isolated) |
Thread Lifecycle
stateDiagram-v2
[*] --> New: Thread created (new)
New --> Runnable: start() called
Runnable --> Running: CPU schedules thread
Running --> Blocked: Waits for I/O/monitor
Running --> Waiting: Thread.sleep() or wait()
Running --> Terminated: run() completes
Blocked --> Runnable: Resource available
Waiting --> Runnable: notify() calledExample: Creating Threads
// Method 1: Extending Thread class
class MyThread extends Thread {
public void run() {
System.out.println("Thread running");
}
}
// Method 2: Implementing Runnable interface (preferred)
class MyRunnable implements Runnable {
public void run() {
System.out.println("Runnable thread running");
}
}
public class Main {
public static void main(String[] args) {
// Using Thread class
MyThread t1 = new MyThread();
t1.start();
// Using Runnable interface
Thread t2 = new Thread(new MyRunnable());
t2.start();
}
}
Trace of t1.start() and t2.start():
| Step | Action | State of t1 |
State of t2 |
|---|---|---|---|
| 1 | t1.start() called |
Runnable | New |
| 2 | t2.start() called |
Running | Runnable |
| 3 | t1.run() completes |
Terminated | Running |
| 4 | t2.run() completes |
Terminated | Terminated |
2. Thread Synchronization
Race Condition Problem
When multiple threads access shared data without synchronization, they may interfere, leading to inconsistent results.
Example: Shared Counter (Race Condition)
class Counter {
private int count = 0;
public void increment() {
count++; // Not thread-safe!
}
public int getCount() {
return count;
}
}
Trace of Race Condition:
| Thread 1 (T1) | Thread 2 (T2) | Final count |
|---|---|---|
count = 0 |
||
count = 1 |
||
count = 1 |
||
count = 2 |
Expected: 2 | |
| Actual: 1 | (Lost update!) |
Solution: synchronized Keyword
public synchronized void increment() {
count++; // Thread-safe
}
Trace After Synchronization:
| T1 Action | T2 Action | count |
|---|---|---|
Locks increment() |
Waits | 0 |
count = 1 |
Waits | 1 |
| Releases lock | Locks increment() |
1 |
count = 2 |
2 |
Other Synchronization Methods
wait()/notify(): Used for thread coordination.ReentrantLock: More flexible thansynchronized.AtomicInteger: Thread-safe counter (no locks needed).
3. Deadlocks and Starvation
Deadlock Scenario
A deadlock occurs when two or more threads are blocked forever, each waiting for a resource held by the other.
Example: Deadlock in Bank Transactions
class Account {
private int balance;
public synchronized void transfer(Account to, int amount) {
if (this == to) return; // Avoid self-transfer
// Deadlock risk: Locking two accounts in different orders!
this.withdraw(amount);
to.deposit(amount);
}
public synchronized void withdraw(int amount) { /* ... */ }
public synchronized void deposit(int amount) { /* ... */ }
}
Trace of Deadlock:
| Thread 1 (T1) | Thread 2 (T2) |
|---|---|
Locks Account A |
Locks Account B |
Tries to lock Account B (waits) |
Tries to lock Account A (waits) |
| Deadlock! | Deadlock! |
How to Avoid Deadlocks?
- Lock Ordering: Always acquire locks in a predefined order.
- Timeouts: Use
tryLock(timeout). - Deadlock Detection: Tools like
ThreadMXBean.
4. Java Concurrency Utilities
Executor Framework
Manages thread pools efficiently.
ExecutorService executor = Executors.newFixedThreadPool(4);
executor.submit(() -> {
System.out.println("Task running in thread pool");
});
executor.shutdown();
Callable and Future
Allows threads to return results.
Future<Integer> future = executor.submit(() -> {
return 42; // Returns Future<Integer>
});
System.out.println("Result: " + future.get()); // Blocks until done
5. Remote Method Invocation (RMI)
What is RMI?
RMI allows objects to invoke methods on remote Java objects (distributed multithreading).
Example: Remote Calculator
// Remote Interface
interface Calculator extends Remote {
int add(int a, int b) throws RemoteException;
}
// Server Implementation
public class CalculatorImpl extends UnicastRemoteObject implements Calculator {
public CalculatorImpl() throws RemoteException {}
public int add(int a, int b) {
return a + b;
}
}
// Client Code
Calculator calc = (Calculator) Naming.lookup("rmi://localhost/Calculator");
int result = calc.add(5, 3); // Calls remote method!
Trace of RMI Call:
| Step | Action | Location |
|---|---|---|
| 1 | Client calls Naming.lookup() |
Client JVM |
| 2 | RMI Registry finds server | RMI Registry |
| 3 | Server executes add(5, 3) |
Server JVM |
| 4 | Result (8) sent back |
Client JVM |
In the Real World
eSewa (Nepal):
- Uses multithreading to handle thousands of online payments simultaneously (e.g., electricity bills, fines).
- Concurrency: Multiple threads process transactions in parallel to avoid delays.
Khalti (Nepal):
- Thread pools manage high-frequency API calls (e.g., merchant transactions, refunds).
- Synchronization: Prevents double-spending by locking accounts during transfers.
WhatsApp (Global):
- Multithreading processes messages, media uploads, and notifications concurrently.
- RMI-like systems (though not pure Java RMI) enable cross-device sync (e.g., phone ↔ web).
Nepal Rastra Bank (NRB) Loan Processing:
- Deadlock avoidance: When multiple branches process loans, lock ordering prevents deadlocks in database updates.
Pathao (Nepal) Ride Matching:
- Thread pools match drivers to riders in real-time (e.g., 10,000+ requests/sec during festivals).
- Atomic counters track available drivers without race conditions.
Exam Tip
- Thread Lifecycle: Always draw the state diagram and explain transitions (e.g.,
start()moves fromNewtoRunnable). - Synchronization: Compare
synchronizedvs.ReentrantLockin terms of flexibility and overhead. - Deadlocks: Always mention the 4 conditions (mutual exclusion, hold & wait, no preemption, circular wait) and how to break them.
- RMI: Know the steps (registry lookup, stub/skeleton, remote call) and exceptions (
RemoteException,NotBoundException). - Code Traces: For every example, show a table of thread states (like the counter example above).
Visual Summary of Key Concepts:
In the real world
eSewa (Nepali payment system) uses thread pools in its backend to handle simultaneous payment requests (e.g., electricity bills, traffic fines). Each payment thread processes transactions independently, reducing wait times during peak hours (e.g., 6–9 PM).
Nepal Rastra Bank’s core banking system employs synchronized blocks to prevent race conditions when updating account balances across multiple ATMs. For example, if two users withdraw ₹10,000 from the same account simultaneously, the bank’s
synchronizedmethods ensure the final balance is correct.Pathao’s ride-matching algorithm uses thread-safe data structures (e.g.,
ConcurrentHashMap) to track driver availability in real-time. When a user requests a ride, multiple threads check driver locations, but the system avoids deadlocks by enforcing a lock order (e.g., always lockingdriverLocationbeforerideRequest).
Based on the TU BIT syllabus for Advanced Java Programming (BIT401), unit 5.
Discussion
Loading…