Multithreading
Threads Basics
Understand process vs thread, thread schedulers, thread safety overview, and basic concurrency concepts.
Interview: Tests fundamental execution models: difference between processes and threads, shared memory layout, context switching, and CPU cores mapping.
A thread is the smallest unit of execution within a program. In Java, multithreading allows a single process to spawn multiple concurrent execution paths, maximizing CPU utilization and enabling responsive, parallel application behavior.
Core Idea
Threads in the same process share the heap and method area, but each maintains its own private stack, PC, and registers.
Why It Matters
Leveraging multiple threads speeds up CPU-intensive computations and prevents blocking UI/network calls.
Interview Lens
Expect deep dives into process vs. thread memory layouts and how context switching impacts performance.
Process vs. Thread Memory Layout
A Process is an isolating execution container allocated its own private memory address space by the operating system. If one process crashes, others are unaffected.
A Thread is an execution path inside a process. Threads are lightweight because they share process resources, leading to:
- Shared Memory: Heap, static fields, class definitions (Metaspace), and open file descriptors. This permits fast data sharing but introduces concurrency risks.
- Private Memory: Each thread gets a private Call Stack (storing local variables and method invocation frames) and a Program Counter (tracking the current instruction index).
The Thread Scheduler and CPU Mapping
Java threads map directly to native operating system threads on modern JVMs (1-to-1 model). The OS Thread Scheduler decides which threads run, when they get context-switched out, and which CPU cores they run on. Scheduling can be preemptive or cooperative; modern systems use preemptive scheduling, allocating time slices to threads.
Code Walkthrough
The following code shows a basic thread setup where a worker executes concurrently alongside the main thread.
public class ThreadBasicsDemo { public static void main(String[] args) { System.out.println("Main starts: " + Thread.currentThread().getName());Thread worker = new Thread(() -> { System.out.println("Worker executes: " + Thread.currentThread().getName()); }, "Worker-Thread");
worker.start(); // Spawns execution path
try { worker.join(); // Main thread blocks until worker finishes } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
System.out.println("Main ends"); } }
Interview-Relevant Information
Q1: What is context switching, and why is it expensive?
Answer: Context switching occurs when the CPU halts one thread and starts running another. The OS must save the CPU registers and program counter state of the current thread, and restore the state of the incoming thread. This takes time, invalidates CPU cache lines, and degrades performance if threads switch too frequently.
Q2: Why are local variables thread-safe in Java?
Answer: Local variables are stored on the thread stack. Each thread has its own call stack and stack frames. Since other threads cannot read or write to another thread's stack, local variables are inherently thread-safe.
Quick Checklist
Can you define the difference between a process and a thread? Do you know what memory is shared and what is private? If yes, you are ready to write basic multithreaded applications.
Use Cases
Running background database cleanup tasks without delaying user response times.
Asynchronous background image rendering and processing pipelines.
Common Mistakes
Calling thread.run() instead of thread.start(). run() executes the task synchronously in the caller thread, bypassing multithreading completely.
Assuming global variables are thread-safe without explicit synchronization.