Lesson 26 • Advanced
Multithreading: Threads, Runnables & Executors
Create and manage threads, understand synchronization, and use thread pools.
Before You Start
You should understand OOP & Interfaces (Lessons 9–11), Lambda Expressions (Lesson 22), and Collections (Lesson 13). Multithreading builds on Runnable interfaces and uses lambdas extensively for task definition.
What You'll Learn
- ✅ Creating threads: Thread class vs Runnable interface
- ✅ Thread lifecycle and states
- ✅ Synchronization and race conditions
- ✅ ExecutorService and thread pools
- ✅ Thread-safe collections
1️⃣ Understanding Concurrency
💡 Analogy: Restaurant Kitchen
A single-threaded program is like a restaurant with one chef — every order waits in line. Multithreading is like having multiple chefs (threads) working simultaneously. But they share the same kitchen (memory), so they need coordination: who uses the stove (synchronization), who gets the knife next (locking), and how to avoid two chefs grabbing the same ingredient (race conditions).
Thread vs Runnable
Always prefer Runnable (or Callable for return values). Java only allows single inheritance, so extending Thread wastes your inheritance slot. Runnable also separates the task from the execution mechanism, making it work with ExecutorService.
Try It: Thread Creation & Lifecycle
// Thread Creation & Lifecycle
console.log("=== Thread Creation ===\n");
// 1. Simulated thread creation
console.log("1. CREATING THREADS:");
class SimThread {
constructor(name, task) { this.name = name; this.task = task; this.state = "NEW"; }
start() {
this.state = "RUNNABLE";
console.log(" [" + this.name + "] State: " + this.state);
this.state = "RUNNING";
console.log(" [" + this.name + "] State: " + this.state);
this.task();
this.stat
...2️⃣ Race Conditions & Synchronization
A race condition occurs when two threads read-modify-write shared data simultaneously. For example, two threads incrementing a counter can both read 5, increment to 6, and write 6 — losing one increment. The synchronized keyword creates a mutex lock that only allows one thread at a time into a critical section.
Try It: Race Conditions & Synchronization
// Race Conditions & Synchronization
console.log("=== Race Conditions ===\n");
// 1. Simulated race condition
console.log("1. RACE CONDITION (simulated):");
let counter = 0;
// In real multi-threading, these would run simultaneously
function unsafeIncrement() {
let temp = counter; // Thread A reads 5
// Thread B also reads 5 here!
counter = temp + 1; // Both write 6 — one increment lost!
}
for (let i = 0; i < 1000; i++) unsafeIncrement();
console.log(" Counter after 1000 i
...3️⃣ ExecutorService & Thread Pools
Creating new Thread() for every task is expensive (~1MB stack per thread) and uncontrolled. ExecutorService manages a pool of reusable threads, queues excess tasks, and provides lifecycle management. In production, you should almost never create threads directly.
Try It: ExecutorService & Producer-Consumer
// ExecutorService & Producer-Consumer
console.log("=== ExecutorService ===\n");
// 1. Thread pool simulation
console.log("1. THREAD POOL (FixedThreadPool):");
class ThreadPool {
constructor(size) { this.size = size; this.tasks = []; this.running = 0; }
submit(name, task) { this.tasks.push({ name, task }); }
execute() {
console.log(" Pool size: " + this.size + " threads");
let threadId = 0;
this.tasks.forEach(({ name, task }) => {
let thread = "T
...Common Mistakes
executor.shutdown() keeps threads alive and your app won't exit.synchronized(new Object()) creates a new lock each time — useless!thread.run() executes on the current thread. thread.start() creates a new thread.Pro Tips
💡 Thread pool sizing: CPU-bound → availableProcessors() threads. I/O-bound → 2x-4x that number.
💡 Prefer AtomicInteger over synchronized for simple counters — lock-free via hardware CAS.
💡 Virtual threads (Java 21+) are lightweight (~KB) and you can create millions — game-changing for I/O.
📋 Quick Reference
| Concept | Java API | Use Case |
|---|---|---|
| Thread pool | Executors.newFixedThreadPool(n) | Reuse threads |
| Synchronize | synchronized keyword | Prevent race conditions |
| Atomic | AtomicInteger, AtomicLong | Lock-free thread safety |
| Concurrent | ConcurrentHashMap | Thread-safe collections |
🎉 Lesson Complete!
You understand Java multithreading fundamentals!
Next: Concurrency Utilities — Locks, Semaphores & Latches.
Sign up for free to track which lessons you've completed and get learning reminders.