Guide to Concurrency and Multithreading
Quick Check: Have you ever wondered why your computer can play music, browse the web, and download files all at the same time? That’s concurrency in action!
Table of Contents
Section titled “Table of Contents”- Understanding Concurrency
- Processes vs Threads
- Java Threading Essentials
- Synchronization Deep Dive
- Threading Problems & Solutions
- Quick Reference
Understanding Concurrency
Section titled “Understanding Concurrency”What’s the Difference?
Section titled “What’s the Difference?”Click to reveal: Concurrency vs Parallelism
Concurrency = Juggling multiple balls (dealing with multiple things at once) Parallelism = Having multiple people each juggle one ball (actually doing multiple things simultaneously)
Visual Comparison
Section titled “Visual Comparison”Single-Core CPU (Concurrency Only)
Section titled “Single-Core CPU (Concurrency Only)”Timeline: 0----1----2----3----4----5----6----7----8Task A: [===] [===] [===]Task B: [===] [===] [===]Task C: [=========]
CPU rapidly switches between tasks - they APPEAR simultaneous
Multi-Core CPU (True Parallelism)
Section titled “Multi-Core CPU (True Parallelism)”Timeline: 0----1----2----3----4----5----6----7----8Core 1: Task A [========================]Core 2: Task B [========================]Core 3: Task C [========================]
Tasks ACTUALLY run at the same time
Benefits & Challenges
Section titled “Benefits & Challenges”Benefits of Concurrency
Benefit | Example | Real-World Impact |
---|---|---|
Better Resource Utilization | While Task A waits for disk read, Task B uses CPU | Up to 80% better performance |
Improved Responsiveness | UI stays active while saving large files | Better user experience |
Higher Throughput | Web server handles 1000s of requests | More users served |
Challenges of Concurrency
Challenge | What Goes Wrong | Simple Analogy |
---|---|---|
Race Conditions | Two threads modify same data | Two people editing same document |
Data Inconsistency | Threads see different values | Bank balance showing different amounts |
Deadlocks | Threads wait for each other forever | Two cars blocking each other |
Processes vs Threads
Section titled “Processes vs Threads”The House Analogy
Section titled “The House Analogy”Think of your computer as a neighborhood:
Process = Entire House
Process (Web Browser)├── Own entrance (memory space)├── Own security system (isolation)├── Own utilities (system resources)└── Residents (threads)
Each house (process) has:
- Own memory space (isolated from neighbors)
- Own resources (files, network connections)
- House number (Process ID)
- At least one resident (main thread)
Thread = Person in House
Thread (Browser Tab)├── Own bedroom (stack)├── Own thoughts (program counter)├── Own notes (registers)└── Shares common areas (heap memory)
People in same house (threads) share:
- Kitchen (heap memory)
- Living room (file handles)
- House resources
But each person has:
- Own bedroom (stack)
- Own thoughts (program counter)
- Own ID (Thread ID)
System Architecture Example
Section titled “System Architecture Example”Computer System├── Process 1 (Web Browser)│ ├── Main UI Thread│ ├── Tab 1 Thread│ ├── Tab 2 Thread│ └── Downloads Thread├── Process 2 (Music Player)│ ├── Audio Thread│ ├── UI Thread│ └── Network Thread└── Process 3 (Text Editor) ├── Main Thread └── Auto-save Thread
Decision Matrix: When to Use What?
Section titled “Decision Matrix: When to Use What?”Use Processes When...
Scenario | Why Process? | Example |
---|---|---|
Need Security | Isolation prevents crashes | Each browser tab as separate process |
Different Apps | Completely separate functionality | Word processor + Media player |
Can Afford Overhead | More memory usage is acceptable | Desktop applications |
Distributed Systems | Running on different machines | Microservices |
Use Threads When...
Scenario | Why Thread? | Example |
---|---|---|
Frequent Data Sharing | Same memory space | GUI app (UI + background work) |
Performance Critical | Lower overhead | Game engine (rendering + physics) |
Coordinated Tasks | Need tight cooperation | Web server handling requests |
Resource Efficient | Less memory usage | Mobile applications |
Java Threading Essentials
Section titled “Java Threading Essentials”Thread Lifecycle Journey
Section titled “Thread Lifecycle Journey”NEW → RUNNABLE → RUNNING → TERMINATED ↓ ↑ ↓ └→ SLEEPING ←→ BLOCKED ←→ WAITING
Thread States Explained
State | What’s Happening | Analogy |
---|---|---|
NEW | Thread created but not started | Person hired but not started work |
RUNNABLE | Ready to run, waiting for CPU | Person ready to work, waiting for assignment |
RUNNING | Currently executing | Person actively working |
SLEEPING | Voluntarily paused for time | Person taking a scheduled break |
BLOCKED | Waiting for lock | Person waiting for meeting room key |
WAITING | Waiting for another thread | Person waiting for colleague to finish |
TERMINATED | Finished execution | Person completed work and left |
Two Ways to Create Threads
Section titled “Two Ways to Create Threads”Method 1: Extending Thread Class
class MyThread extends Thread { @Override public void run() { System.out.println("Thread running: " + getName()); }}
// UsageMyThread thread = new MyThread();thread.start(); // Launch the thread
Method 2: Implementing Runnable (Recommended)
class MyTask implements Runnable { @Override public void run() { System.out.println("Task running: " + Thread.currentThread().getName()); }}
// UsageThread thread = new Thread(new MyTask());thread.start(); // Launch the thread
Why Runnable is better: Java single inheritance limitation - you can implement multiple interfaces!
Thread Control Methods
Section titled “Thread Control Methods”Essential Thread Methods
Method | Purpose | Example Use Case |
---|---|---|
start() | Begin thread execution | Launch background task |
join() | Wait for thread to finish | Wait for file download before processing |
sleep() | Pause for specified time | Animation delays |
interrupt() | Signal thread to stop | Cancel long-running operation |
setPriority() | Set thread importance | UI threads get higher priority |
Special Thread Types
Section titled “Special Thread Types”Daemon Threads
What are they? Background helper threads that die when main program ends.
Thread daemonThread = new Thread(() -> { while (true) { System.out.println("Cleaning up..."); try { Thread.sleep(1000); } catch (InterruptedException e) { break; } }});daemonThread.setDaemon(true); // Make it a daemondaemonThread.start();
// When main program ends, daemon thread automatically stops
Real Examples:
- Garbage collector
- Timer threads
- Monitoring threads
Synchronization Deep Dive
Section titled “Synchronization Deep Dive”The Problem: Race Conditions
Section titled “The Problem: Race Conditions”What Goes Wrong Without Synchronization?
class BankAccount { private int balance = 1000;
// DANGER: Not synchronized! public void withdraw(int amount) { if (balance >= amount) { // Thread A checks: 1000 >= 500 ✓ // Thread B checks: 1000 >= 700 ✓ balance = balance - amount; // Thread A: 1000 - 500 = 500 // Thread B: 1000 - 700 = 300 } }}
// Result: Balance could be 500 OR 300, depending on timing!// Expected: Should reject one withdrawal when balance < 700
Solution 1: Synchronized Methods
Section titled “Solution 1: Synchronized Methods”Method-Level Synchronization
class BankAccount { private int balance = 1000;
// Only ONE thread can access this method at a time public synchronized void withdraw(int amount) { if (balance >= amount) { System.out.println("Withdrawing: " + amount); balance = balance - amount; System.out.println("New balance: " + balance); } else { System.out.println("Insufficient funds!"); } }
public synchronized int getBalance() { return balance; // Also synchronized for consistency }}
Key Points:
- Locks the entire object for the method duration
- Other synchronized methods must wait
- Thread-safe but potentially slower
Solution 2: Synchronized Blocks (Better!)
Section titled “Solution 2: Synchronized Blocks (Better!)”Block-Level Synchronization
class BankAccount { private int balance = 1000; private String accountInfo = "John Doe";
public void withdraw(int amount) { // Multiple threads can enter method
System.out.println("Processing withdrawal request...");
synchronized(this) { // Only critical section is locked if (balance >= amount) { balance = balance - amount; } } // Lock released immediately
System.out.println("Transaction logged"); }}
Advantages:
- Faster: Only locks critical sections
- More precise control
- Better concurrency
Advanced: Fine-Grained Synchronization
Section titled “Advanced: Fine-Grained Synchronization”Multiple Locks for Different Resources
public class SmartBankAccount { private int balance = 1000; private String accountInfo = "John Doe"; private List<String> transactionHistory = new ArrayList<>();
// Separate locks for different resources private final Object balanceLock = new Object(); private final Object infoLock = new Object(); private final Object historyLock = new Object();
public void withdraw(int amount) { synchronized(balanceLock) { // Only locks balance operations if (balance >= amount) { balance -= amount; } } }
public void updateInfo(String newInfo) { synchronized(infoLock) { // Different lock for account info accountInfo = newInfo; } }
public void addTransaction(String transaction) { synchronized(historyLock) { // Another lock for history transactionHistory.add(transaction); } }
// These three methods can run SIMULTANEOUSLY!}
Performance Boost: Operations on different data can run in parallel!
Reentrant Synchronization
Section titled “Reentrant Synchronization”Same Thread, Multiple Locks
class ReentrantExample { public synchronized void methodA() { System.out.println("In methodA"); methodB(); // Can same thread call another synchronized method? }
public synchronized void methodB() { System.out.println("In methodB"); // YES! Same thread can acquire same lock multiple times }}
Why it works: Java tracks lock ownership by thread, not just lock acquisition.
Threading Problems & Solutions
Section titled “Threading Problems & Solutions”Problem 1: Deadlock - “Mexican Standoff”
Section titled “Problem 1: Deadlock - “Mexican Standoff””The Classic Deadlock Scenario
class DeadlockDemo { private final Object lock1 = new Object(); private final Object lock2 = new Object();
public void method1() { synchronized(lock1) { // Thread A gets lock1 System.out.println("Thread A: Got lock1");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized(lock2) { // Thread A waits for lock2 System.out.println("Thread A: Got lock2"); } } }
public void method2() { synchronized(lock2) { // Thread B gets lock2 System.out.println("Thread B: Got lock2");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized(lock1) { // Thread B waits for lock1 System.out.println("Thread B: Got lock1"); } } }}
// Result: Both threads wait forever!
Deadlock Prevention: Lock Ordering
class DeadlockFixed { private final Object lock1 = new Object(); private final Object lock2 = new Object();
public void method1() { synchronized(lock1) { // Always acquire lock1 first synchronized(lock2) { // Then lock2 System.out.println("Thread A: Both locks acquired"); } } }
public void method2() { synchronized(lock1) { // Same order: lock1 first synchronized(lock2) { // Then lock2 System.out.println("Thread B: Both locks acquired"); } } }}
// Result: Sequential execution, no deadlock!
Golden Rule: Always acquire locks in the same order!
Problem 2: Starvation - “Never My Turn”
Section titled “Problem 2: Starvation - “Never My Turn””When Low Priority Threads Never Get Resources
class StarvationExample { public synchronized void importantWork() { // High priority threads keep calling this System.out.println("High priority work"); }
public synchronized void backgroundWork() { // Low priority threads never get a chance System.out.println("Background work"); }}
Solutions:
- Use fair locks:
ReentrantLock(true)
- Time-bounded waiting
- Priority scheduling algorithms
Problem 3: Livelock - “After You!” “No, After You!”
Section titled “Problem 3: Livelock - “After You!” “No, After You!””The Overly Polite Threads
class LivelockExample { static class PoliteWorker { private String name; private boolean active;
public PoliteWorker(String name) { this.name = name; this.active = true; }
public void work(PoliteWorker colleague) { while (active) { if (colleague.active) { System.out.println(name + ": After you, " + colleague.name); active = false; // I'll step back
try { Thread.sleep(100); } catch (InterruptedException e) {}
active = true; // Let me try again } } } }
// Both workers keep stepping aside for each other! // They're not blocked, but no work gets done}
Solution: Add randomization or timeout to break the cycle.
Quick Reference
Section titled “Quick Reference”Thread States Cheat Sheet
Section titled “Thread States Cheat Sheet”NEW ──start()──→ RUNNABLE ──scheduler──→ RUNNING ↑ ↓ WAITING ←──notify()──── SLEEPING ↑ ↓ BLOCKED ←──────────────→ TERMINATED
Synchronization Quick Guide
Section titled “Synchronization Quick Guide”Technique | Use When | Pros | Cons |
---|---|---|---|
synchronized method | Simple cases | Easy to use | Can be slow |
synchronized block | Most cases | Precise control | Need to choose lock object |
Multiple locks | Complex objects | Better performance | Deadlock risk |
Problem Prevention Checklist
Section titled “Problem Prevention Checklist”- Deadlock: Always acquire locks in same order
- Starvation: Use fair locks or time limits
- Livelock: Add randomization to retry logic
- Race Conditions: Synchronize shared resource access
Best Practices Summary
Section titled “Best Practices Summary”- Minimize synchronized sections - Lock only what you must
- Prefer Runnable over Thread - Better flexibility
- Use private lock objects - Avoid external interference
- Don’t hold locks longer than necessary - Release quickly
- Test thoroughly - Concurrency bugs are sneaky!
Congratulations! You’ve mastered the fundamentals of Java concurrency and multithreading. Remember: with great power comes great responsibility - use synchronization wisely!
Next Steps:
- Explore
java.util.concurrent
package - Learn about Thread Pools and ExecutorService
- Study Lock-free programming techniques
- Practice with real-world concurrency problems