Cosmic Module
J
Qubits of DPK
March 14, 2026
Core Java
Layman Explanation
A restaurant kitchen has multiple chefs working simultaneously — one making soup, one grilling, one preparing dessert. That's multithreading — multiple tasks executing concurrently in the same program.
What is a Thread?
The smallest unit of execution within a process. A program can have multiple threads running concurrently.
javascript
QUBITS OF DPK
Creating Threads — Two Ways
Way 1: Extend Thread
java
QUBITS OF DPK
Way 2: Implement Runnable (Preferred)
java
QUBITS OF DPK
Why Runnable over Thread?
javascript
QUBITS OF DPK
Thread Priority & Sleep
java
QUBITS OF DPK
Race Condition
When multiple threads access shared data simultaneously and the final result depends on thread execution order.
java
QUBITS OF DPK
Fix: synchronized
java
QUBITS OF DPK
Thread States
javascript
QUBITS OF DPK
Production Use Case
java
QUBITS OF DPK
️ All Traps
java
QUBITS OF DPK
Interview Answer (SDE-2)
"A thread is the smallest unit of execution. Java creates threads by extending Thread or implementing Runnable — Runnable is preferred since Java supports single class inheritance and it separates task logic from thread mechanics. start() creates a new thread and calls run(); calling run() directly just executes in the current thread. Race conditions occur when multiple threads access shared mutable state — synchronized keyword ensures only one thread enters a block at a time. Thread states: New, Runnable, Running, Blocked, Waiting, Timed Waiting, Terminated. In production, always use ExecutorService thread pools rather than raw threads."
Interview Questions & MAANG-Level Answers
Q1. What is the difference between Thread and Runnable?
Thread is a class; Runnable is a functional interface. Extending Thread: class MyTask extends Thread — can't extend any other class (single inheritance limit). Implementing Runnable: class MyTask implements Runnable — can still extend another class, and separates task logic from threading mechanism. Runnable is preferred: same task can run on different threads, works with ExecutorService, and is more testable. Lambda makes Runnable even simpler: new Thread(() -> doWork()).start().
Q2. What is the difference between start() and run()?
start() creates a NEW OS thread and calls run() in that thread — truly concurrent execution. run() executes in the CURRENT thread — no new thread created, sequential execution. Critical mistake: t1.run() appears to work but executes synchronously in the calling thread. Always use start() for multithreading. You can verify: Thread.currentThread().getName() inside run() — with start() it shows "Thread-0"; with run() it shows "main".
Q3. What is a race condition and how do you fix it?
Race condition: when multiple threads access shared mutable data simultaneously and the result depends on thread execution order. Example: count++ is 3 operations (read, increment, write) — two threads can both read 5, both increment to 6, both write 6 — increment lost. Fixes: (1) synchronized method/block — mutual exclusion. (2) AtomicInteger.incrementAndGet() — lock-free atomic operation. (3) volatile — for visibility but not atomicity. (4) Immutable objects — no state to race on.
Q4. What is synchronized keyword?
synchronized ensures only ONE thread executes the protected code at a time. Works via an intrinsic lock (monitor) on an object. Methods: synchronized void method() — locks on this. Blocks: synchronized(lockObject) {} — finer-grained control. Drawback: serializes access, reduces concurrency. Alternative: ReentrantLock for tryLock, timeout, and fairness. In production: prefer ConcurrentHashMap over Hashtable, AtomicInteger over synchronized counters for better throughput.
Q5. What is a deadlock?
Deadlock: two or more threads are permanently blocked, each waiting for a lock held by the other:
javascript
QUBITS OF DPK
Prevention: (1) Always acquire locks in the same ORDER across all threads. (2) Use tryLock() with timeout. (3) Use ReentrantLock. (4) Minimize synchronized scope. Detection: thread dumps (jstack), JVM profilers. In production, deadlocks are notoriously hard to reproduce — design lock ordering upfront.
Q6. What are thread states?
New → thread created, not started. Runnable → start() called, ready to run (includes running on CPU). Blocked → waiting to acquire a synchronized lock. Waiting → wait() or join() called, indefinite wait. Timed_Waiting → sleep(ms), wait(ms), join(ms) — waiting with timeout. Terminated → run() completed or exception. You can check state via thread.getState(). Thread dumps show all thread states — critical for debugging production issues.
Q7. What is ExecutorService and why use it over raw threads?
ExecutorService is a thread pool manager in java.util.concurrent. Problems with raw threads: creating/destroying threads is expensive (MB per thread), uncontrolled thread count can crash JVM. ExecutorService maintains a pool of reusable threads:
java
QUBITS OF DPK
Benefits: thread reuse (no create/destroy overhead), bounded concurrency (max 10 threads), task queuing, Future for results, graceful shutdown. In production (Spring Boot), use @Async with configured ThreadPoolTaskExecutor.
Q8. What is the difference between sleep() and wait()?
sleep(): Thread.sleep(ms) — pauses current thread for specified time, does NOT release any locks held, resumes automatically after timeout. wait(): Object.wait() — called on a lock object inside synchronized block, RELEASES the lock, pauses until notify()/notifyAll() is called (plus optional timeout). sleep() is for time-based pauses; wait() is for inter-thread communication (producer-consumer pattern). wait() must be in synchronized block; sleep() can be anywhere.