- Understand what threads are and how they differ from processes
- Learn the benefits and challenges of multithreading
- Distinguish between concurrency and parallelism
- Know when to use multithreading in applications
Introduction to Threads in Java
Restaurant scenario: one waiter serves all tables sequentially—everyone waits. Multiple waiters working simultaneously? Tables get served faster. That's multithreading.
A thread is the smallest unit of execution. Your program typically runs as a single sequence (single-threaded). Multithreading lets multiple sequences run concurrently, using multi-core processors better.
What is a Thread?
A thread is an independent path of execution. Every Java program has at least one—the main thread, starting when main() begins.
public class MainThreadExample {
public static void main(String[] args) {
// This code runs on the "main" thread
Thread current = Thread.currentThread();
System.out.println("Current thread: " + current.getName()); // "main"
}
}
Threads vs Processes
| Aspect | Process | Thread |
|---|---|---|
| Definition | Independent program with its own memory | Execution path within a process |
| Memory | Separate memory space | Shared memory within process |
| Communication | Inter-process communication (IPC) | Direct access to shared data |
| Overhead | Heavy (creating process is expensive) | Light (creating thread is cheaper) |
| Isolation | Crash in one doesn't affect others | Crash can affect entire process |
A Java application is a process. Within that process, you can create multiple threads that share the same memory space.
Why Use Multithreading?
1. Responsiveness
In a GUI application, a long-running task on the main thread freezes the interface. Running it on a separate thread keeps the UI responsive:
// BAD: UI freezes during download
public void onButtonClick() {
downloadLargeFile(); // Takes 30 seconds - UI frozen!
updateProgress();
}
// GOOD: Download runs in background
public void onButtonClick() {
new Thread(() -> {
downloadLargeFile(); // Runs in background
updateProgress(); // UI stays responsive
}).start();
}
2. Performance (Parallelism)
Modern CPUs have multiple cores. Multithreading lets you use them all:
// Sequential: Uses one core
for (Image img : images) {
process(img); // If 100 images, 1 second each = 100 seconds
}
// Parallel: Uses all cores
images.parallelStream()
.forEach(this::process); // On 4 cores = ~25 seconds
3. Simplified Modeling
Some problems naturally decompose into concurrent activities:
- A web server handling multiple client requests
- A game updating physics, rendering, and AI simultaneously
- A file indexer scanning multiple directories at once
The Main Thread
Every Java program starts with the main thread:
public class MainThreadDemo {
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
System.out.println("Thread name: " + mainThread.getName()); // main
System.out.println("Thread ID: " + mainThread.getId()); // 1
System.out.println("Thread priority: " + mainThread.getPriority()); // 5
System.out.println("Is alive: " + mainThread.isAlive()); // true
System.out.println("Is daemon: " + mainThread.isDaemon()); // false
}
}
The main thread:
- Is created by the JVM when the program starts
- Executes the
main()method - Is the parent of all other threads you create
- Program exits when the main thread (and all non-daemon threads) complete
Concurrency vs Parallelism
These terms are related but different:
Concurrency: Multiple tasks making progress, possibly by time-slicing on a single core
Thread 1: ████----████----████
Thread 2: ----████----████----
Time →
(Threads take turns on one core)
Parallelism: Multiple tasks executing simultaneously on different cores
Core 1: ████████████████████
Core 2: ████████████████████
Time →
(True simultaneous execution)
Java's threading provides concurrency. Whether you get true parallelism depends on:
- Number of CPU cores
- JVM and OS scheduling
- Nature of your tasks (CPU-bound vs I/O-bound)
Challenges of Multithreading
Multithreading introduces complexity:
1. Race Conditions
When multiple threads access shared data, results can be unpredictable:
// Shared counter
int counter = 0;
// Thread 1 and Thread 2 both do this:
counter++; // Not atomic! Read-Modify-Write
// Expected: counter = 2
// Actual: counter might be 1 (race condition!)
2. Deadlocks
Threads waiting for each other forever:
// Thread 1 holds Lock A, waits for Lock B
// Thread 2 holds Lock B, waits for Lock A
// Neither can proceed - DEADLOCK!
3. Thread Safety
Code that works correctly with single thread may fail with multiple threads:
// Not thread-safe
ArrayList<String> list = new ArrayList<>();
// Multiple threads adding simultaneously = data corruption
4. Debugging Difficulty
Threading bugs are often:
- Non-deterministic (appear randomly)
- Timing-dependent (disappear when you add print statements)
- Hard to reproduce
Thread Properties
Every thread has several properties:
| Property | Description | Default |
|---|---|---|
| Name | Human-readable identifier | "Thread-0", "Thread-1", etc. |
| ID | Unique numeric identifier | Assigned by JVM |
| Priority | Scheduling hint (1-10) | 5 (NORM_PRIORITY) |
| Daemon | Background thread that doesn't prevent exit | false |
| State | Current lifecycle state | NEW, RUNNABLE, etc. |
Thread thread = new Thread(() -> { /* work */ });
thread.setName("WorkerThread");
thread.setPriority(Thread.MAX_PRIORITY); // 10
thread.setDaemon(true); // Background thread
Daemon Threads
Daemon threads are background threads that don't prevent the JVM from exiting:
Thread daemon = new Thread(() -> {
while (true) {
cleanupTemporaryFiles();
sleep(60000); // Every minute
}
});
daemon.setDaemon(true); // Mark as daemon
daemon.start();
// When main thread ends, daemon thread is killed automatically
Use daemon threads for:
- Background cleanup tasks
- Monitoring/heartbeat threads
- Any thread that shouldn't keep the application running
When to Use Multithreading
| Use Multithreading | Avoid Multithreading |
|---|---|
| Long-running I/O operations | Simple, fast computations |
| Independent parallel tasks | Heavily interdependent tasks |
| Responsive UIs | When single-threaded code is fast enough |
| Server handling multiple clients | When complexity outweighs benefits |
| CPU-intensive tasks on multi-core | Simple scripts and utilities |
Threads are independent execution paths. Every Java program starts with a main thread. Threads share memory within a process. Concurrency means multiple tasks making progress (time-slicing). Parallelism is true simultaneous execution on multiple cores. Daemon threads are background threads that don't prevent exit. Multithreading improves responsiveness but introduces complexity—race conditions, debugging challenges. Use when benefits outweigh the cost.
