Part I
Java Multithreading
1. Fundamentals & Thread Lifecycle
Quick Reference
Basics
Process, Thread, Lifecycle, Creation
Safety
Race Condition, Sync, Volatile, Atomic, Locks
Coordination
wait/notify, Latch, Barrier, Semaphore
Modern Java
Executor, CompletableFuture, ForkJoin, Concurrent Collections
Legend
Core Concept
Sync / Safety
Executor / Async
Collections / Utilities
Danger Zone (Deadlock/Starvation)
Core Definitions (Say These First)
Process→An independently running application with its own memory space
Thread→Smallest unit of execution, runs inside a process, shares its memory
Multithreading→Multiple threads run concurrently within one process, sharing memory
Concurrency→Multiple tasks make progress in overlapping time (not necessarily the same instant)
Parallelism→Multiple tasks run truly simultaneously on multiple CPU cores
Thread Lifecycle
State Transitions
NEW
→ start() →
RUNNABLE
→
RUNNING
RUNNING
→
BLOCKED / WAITING / TIMED_WAITING
→
TERMINATED
| State | Meaning |
| NEW | Thread object created, start() not yet called |
| RUNNABLE | Eligible to run — moves here after start() |
| RUNNING | CPU is actively executing this thread |
| BLOCKED | Waiting to acquire a monitor lock held by another thread |
| WAITING | Waiting indefinitely — wait(), join() without timeout |
| TIMED_WAITING | Waiting a fixed time — sleep(), wait(ms), join(ms) |
| TERMINATED | Run completed (normally or via exception) |
2. Thread Creation
Thread Creation Options
| Option | Notes |
| extends Thread | Simplest, but no multiple inheritance — avoid in practice |
| implements Runnable | Preferred — no return value, no checked exceptions |
| Callable<V> | Returns a value, can throw checked exceptions |
| Future<V> | Handle to result of async task — get() blocks |
| CompletableFuture | Non-blocking, chainable, combinable async pipeline |
ReturnNo return value
ExceptionsNo checked exceptions
Methodrun()
ReturnReturns a value (V)
ExceptionsCan throw checked exceptions
Methodcall()
3. Thread Safety — Race Condition, Sync, Visibility, Atomicity
Race Condition & Synchronization
| Race Condition | ≥2 threads read-modify-write same shared state concurrently → lost updates |
| Critical Section | Code block touching shared state; must run exclusively |
| synchronized method | Locks this (instance monitor) for the whole method |
| synchronized block | synchronized(obj){...} — locks only the critical section |
| static synchronized | Locks the Class object — one lock across all instances |
| Monitor / Intrinsic Lock | Built-in lock every Java object has; backbone of synchronized |
Visibility & Atomicity
| volatile | Guarantees visibility of latest value across threads — NOT atomicity |
| AtomicInteger / AtomicLong | Lock-free, thread-safe counters via incrementAndGet() etc. |
| AtomicReference | Atomic updates to object references |
| CAS | Compare-And-Swap: read → compare expected → update or retry (lock-free) |
Common Comparisons
GuaranteesVisibility only
CASNo CAS
count++Unsafe
GuaranteesVisibility + Atomicity
CASUses CAS internally
count++Safe
4. Locking Mechanisms & Deadlock Management
Locking Mechanisms
| ReentrantLock | Explicit lock — tryLock(), fairness, interruptibility |
| ReadWriteLock | Multiple readers OR one writer — great for read-heavy caches |
| StampedLock | Adds optimistic read mode — faster than ReadWriteLock |
| Fair Lock | new ReentrantLock(true) — grants lock in request order |
| tryLock() | Attempts lock, returns immediately/with timeout instead of blocking |
Deadlock, Starvation, Livelock
| Deadlock | Threads hold-and-wait on each other's locks in a cycle → all stuck forever |
| Starvation | A thread never gets CPU/lock because others keep getting priority |
| Livelock | Threads keep responding to each other but make no real progress |
| Prevention | Consistent lock ordering, tryLock()+timeout, avoid nested locks |
Classic Deadlock Example
// Thread-1
lock(A); lock(B);
// Thread-2 (reverse order = deadlock)
lock(B); lock(A);
ReentrantLock with tryLock
Lock lock = new ReentrantLock();
if (lock.tryLock(5, TimeUnit.SECONDS)) {
try { /* work */ }
finally { lock.unlock(); }
}
5. Thread Communication — wait() / notify() / notifyAll() / sleep()
| Method | Behavior |
| wait() | Releases the lock and waits until notified — must be inside a synchronized block (Object class) |
| notify() | Wakes ONE waiting thread (unspecified which) |
| notifyAll() | Wakes ALL waiting threads |
| sleep() | Pauses the thread but KEEPS the lock (Thread class, not Object) |
wait() / notifyAll()
synchronized(lock) {
while (!ready) lock.wait();
// proceed after notifyAll()
}
wait() vs sleep()
// wait(): releases lock, Object class, needs synchronized
// sleep(): keeps lock, Thread class, no synchronized needed
6. Executor Framework & Thread Pools
Executor Framework
| Executor | Base interface — just execute(Runnable) |
| ExecutorService | Adds submit(), shutdown(), returns Future |
| ScheduledExecutorService | Run tasks after a delay / periodically |
| FixedThreadPool | Fixed N threads, unbounded queue |
| CachedThreadPool | Grows/shrinks dynamically — risky under heavy load |
| SingleThreadExecutor | One thread, tasks run sequentially in order |
| ScheduledThreadPool | Fixed pool for scheduled/repeating jobs |
Production: ThreadPoolTaskExecutor
@Bean
Executor taskExecutor() {
ThreadPoolTaskExecutor ex =
new ThreadPoolTaskExecutor();
ex.setCorePoolSize(10);
ex.setMaxPoolSize(20);
ex.setQueueCapacity(100);
ex.initialize();
return ex;
}
Why not new Thread()? No pooling/reuse, no lifecycle management, no backpressure/queueing, resource exhaustion risk in production.
7. Concurrent Collections & Producer-Consumer
Concurrent Collections
| ConcurrentHashMap | Bucket/node-level locking — thread-safe map, no full-map lock |
| CopyOnWriteArrayList | Copies array on write — best when reads >> writes |
| ConcurrentLinkedQueue | Lock-free, non-blocking FIFO queue |
| BlockingQueue | Blocks on put()/take() when full/empty — Producer-Consumer |
| ConcurrentSkipListMap | Thread-safe sorted map, concurrent version of TreeMap |
Producer-Consumer (BlockingQueue)
BlockingQueue<Integer> q =
new ArrayBlockingQueue<>(10);
q.put(1); // producer, blocks if full
q.take(); // consumer, blocks if empty
ConcurrentHashMap
ConcurrentHashMap<String,String> map =
new ConcurrentHashMap<>();
map.computeIfAbsent("k", k -> "v");
8. Synchronization Utilities
| Utility | Purpose |
| CountDownLatch | One-time — main thread awaits N workers finishing (not reusable) |
| CyclicBarrier | Reusable — all threads wait for each other at a checkpoint |
| Semaphore | Limits N concurrent threads — DB pool, rate limiting |
| Phaser | Advanced reusable barrier with dynamic party registration |
| Exchanger | Two threads swap data at a synchronization point |
CountDownLatch
CountDownLatch latch =
new CountDownLatch(3);
latch.countDown(); // per worker
latch.await(); // main thread
Semaphore (Rate Limiting)
Semaphore sem = new Semaphore(5);
sem.acquire();
try { /* limited resource */ }
finally { sem.release(); }
CountDownLatch vs CyclicBarrier — CountDownLatch is one-time use, parent thread waits; CyclicBarrier is reusable, all threads wait for each other.
9. Fork-Join, Parallel Streams & CompletableFuture
CompletableFuture Chain (Aggregator Pattern)
supplyAsync()
→
thenApply()
→
thenCombine()
→
exceptionally()
Async Task → Transform → Combine Results → Handle Errors
CompletableFuture<User> u =
CompletableFuture.supplyAsync(
() -> userService.getUser());
CompletableFuture<Order> o =
CompletableFuture.supplyAsync(
() -> orderService.getOrders());
CompletableFuture.allOf(u, o).join();
ForkJoinPool & Parallel Stream
| ForkJoinPool | Divide-and-conquer pool with work-stealing between idle threads |
| RecursiveTask<V> | Fork-join task that returns a result |
| RecursiveAction | Fork-join task with no return value |
| Work Stealing | Idle worker threads "steal" tasks from busy threads' queues |
| parallelStream() | Uses common ForkJoinPool internally — good for CPU-bound work only |
Avoid parallelStream() for: DB Calls, External APIs, Small Collections.
10. ThreadLocal & Spring Boot @Async
ThreadLocal
| ThreadLocal<T> | Per-thread isolated copy of a variable — no sharing across threads |
| InheritableThreadLocal | Child threads inherit parent's ThreadLocal value |
| MDC (Logging) | Uses ThreadLocal to carry correlation/request-ID through logs |
Use cases: User Context, Correlation ID, Request Tracking. Always call .remove() in thread pools to avoid leaks.
Spring Boot @Async
@EnableAsync
@Async
public CompletableFuture<String> process() {
return CompletableFuture
.completedFuture("Done");
}
⚠ Will @Async work in a same-class self-invocation call? NO — Spring AOP proxy is bypassed; the method must be called through the Spring-managed bean.
11. Thread Pool Sizing & Kafka Concurrency
Thread Pool Sizing (Frequently Asked)
CPU-Bound Tasks
Threads = CPU Cores + 1
e.g. heavy computation, encryption, image processing
I/O-Bound Tasks
Threads = Cores × (1 + Wait/Compute)
e.g. DB calls, REST API calls, file I/O — usually need larger pools
Kafka Consumer Concurrency (Partitions ≥ Consumers)
@KafkaListener(topics = "orders", concurrency = "3")
// 6 Partitions, 3 Consumers
Consumer-1 → P0, P1
Consumer-2 → P2, P3
Consumer-3 → P4, P5
One partition is consumed by exactly one consumer within a group — extra consumers beyond partition count sit idle.
12. Production Troubleshooting
High CPU→Thread dump + profiler to find hot loops/threads
Deadlock→Thread dump shows "waiting to lock" cycle
Thread Dump→Snapshot of all thread states/stacks at a point in time
Heap Dump→Snapshot of memory objects — for memory leak analysis
Starvation→Thread never scheduled — check fairness / priorities
Pool Exhaustion→All pool threads busy — queue grows, requests time out
13. Interview Comparisons & Must-Know Code Snippets
| synchronized | ReentrantLock |
| JVM managed, auto-release | Manual lock/unlock (try/finally) |
| No tryLock() | tryLock() with timeout |
| Less flexible | Fair locking, interruptible |
| wait() | sleep() |
| Releases the lock | Keeps the lock |
| Object class | Thread class |
| Needs synchronized | No synchronized needed |
| HashMap | ConcurrentHashMap |
| Not thread-safe | Thread-safe, bucket-level locks |
| Race conditions possible | Safe concurrent access |
| CountDownLatch | CyclicBarrier |
| One-time use | Reusable |
| Main thread waits for workers | All threads wait for each other |
Runnable Task
class MyTask implements Runnable {
public void run() {
System.out.println("Running");
}
}
Callable + Future
Callable<String> task = () -> "Success";
Future<String> f = executor.submit(task);
String result = f.get(); // blocks
Synchronized Method / Block
public synchronized void increment() { count++; }
synchronized(this) { count++; }
Volatile Flag (Safe Stop)
private volatile boolean running = true;
// running = false; from another thread
// visible immediately to the loop thread
AtomicInteger Counter
AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet();
count.getAndIncrement();
ExecutorService Basics
ExecutorService ex =
Executors.newFixedThreadPool(10);
ex.submit(task);
ex.shutdown();
ThreadLocal Context
ThreadLocal<String> user =
new ThreadLocal<>();
user.set("Pranik"); user.get();
user.remove(); // avoid leaks in pools
CompletableFuture Chain
CompletableFuture.supplyAsync(this::callService)
.thenApply(this::process)
.exceptionally(ex -> "Fallback");
14. Multithreading — 30-Second Interview Revision
✓Concurrency ≠ Parallelism (overlap vs simultaneous)
✓Thread: NEW→RUNNABLE→RUNNING→(BLOCKED/WAITING/TIMED_WAITING)→TERMINATED
✓Prefer Runnable/Callable over extending Thread
✓Race Condition = unsynchronized shared-state read-modify-write
✓volatile = visibility only, NOT atomicity
✓Atomic classes use CAS — lock-free thread safety
✓ReentrantLock > synchronized: tryLock(), fairness
✓Deadlock = circular wait on locks → prevent via lock ordering
✓wait() releases lock; sleep() keeps lock
✓Avoid new Thread() in enterprise code — use ExecutorService
✓CPU-bound pool size = cores + 1
✓I/O-bound pool size = cores × (1 + wait/compute)
✓parallelStream() uses common ForkJoinPool — avoid for I/O
✓CompletableFuture > Future: non-blocking, chainable
✓ConcurrentHashMap: bucket-level locks, no full-map lock
✓CopyOnWriteArrayList: best when reads >> writes
✓CountDownLatch = one-time; CyclicBarrier = reusable
✓Semaphore = limit N concurrent threads (DB pool, rate limit)
✓ThreadLocal = per-thread isolated data (context, correlation ID)
✓@Async fails on self-invocation — AOP proxy bypassed
✓Kafka: partitions ≥ consumers; extra consumers idle
✓Troubleshooting: thread dump for deadlock/blocking, heap dump for memory
Part II
Exception Handling
15. Exception Handling Basics & Throwable Hierarchy
Throwable Hierarchy
Object
└── Throwable (message + cause + stack trace)
├── Error — serious JVM problems, usually unrecoverable
└── Exception — application can handle/recover
├── Checked Exception — checked at compile time
└── RuntimeException — Unchecked Exception, checked at runtime
Without exception handling: error occurs → program terminates. With it: error occurs → exception caught → alternative logic runs → application continues.
Error vs Exception
| Error | Exception |
| Serious JVM problem, app should not handle | Unexpected but recoverable situation |
| OutOfMemoryError, StackOverflowError, NoClassDefFoundError | IOException, SQLException, ArithmeticException, NullPointerException |
Purpose of exception handling: maintain normal program flow, prevent crashes, improve reliability, meaningful error messages, simplify debugging.
16. Checked vs Unchecked Exceptions
Checked AtCompile Time
HandlingCompiler forces try-catch OR throws
NatureRecoverable
ExamplesIOException, SQLException, ClassNotFoundException, FileNotFoundException, InterruptedException, ParseException
Checked AtRuntime
HandlingCompiler does NOT force handling
NatureUsually a programming mistake
ExamplesNullPointerException, ArithmeticException, ArrayIndexOutOfBoundsException, IllegalArgumentException, NumberFormatException, ClassCastException, ConcurrentModificationException
Real Project Scenarios
IOException→Reading a CSV file
SQLException→Database query failure
InterruptedException→Background job interrupted
NumberFormatException→Invalid user input
NullPointerException→Missing API response field
ArithmeticException→Division by zero in business calculation
17. try / catch / finally — Execution Flow
| Block | Rule |
| try | Contains risky code; only one try block before catch/finally |
| catch | Handles a specific exception; can have multiple catch blocks, ordered specific → generic |
| finally | Always executes (exception or not); used for cleanup — close resources, release connections |
| Multi-Catch (Java 7+) | catch(IOException | SQLException e) — less code, better readability |
Execution Flow
try
↓ No Exception
Skip catch → finally → Continue
↓ Exception Occurs
Matching catch → finally → Continue
return inside try: finally still executes, then the method returns. System.exit() terminates the JVM — finally will NOT execute. Only one finally is allowed per try.
final vs finally vs finalize()
final→Keyword — cannot change (variable/method/class)
finally→Block — always executes
finalize()→Method — called before GC, deprecated, don't use
18. throw vs throws & Exception Propagation
TypeKeyword — actually throws one exception
UsedInside a method body, creates an exception object
Syntaxthrow new Exception(...);
TypeDeclares possible exceptions in method signature
UsedCan declare multiple, does not itself throw anything
Syntaxvoid readFile() throws IOException
Exception Propagation Flow
main()
→
service()
→
repository()
→
Exception thrown
→ propagates back →
Handled at main()/service()
Checked exceptions must be handled or declared at every level. Unchecked exceptions automatically propagate until caught.
19. Custom & Business Exceptions
Custom Exception Hierarchy
Exception
└── BusinessException
├── UserNotFoundException
├── PaymentFailedException
└── CourseNotFoundException
Create custom exceptions for: User Not Found, Insufficient Balance, Course Already Exists, Certificate Expired, Invalid OTP, Payment Failed.
ExtendsException
HandlingCompiler forces handling, recoverable
ExtendsRuntimeException
HandlingPreferred for business validation — Spring Boot standard
Technical vs Business Exception
Technical→IOException, SQLException, Network Failure, Timeout
Business→Duplicate User, Invalid Password, Invalid OTP, Course Closed
20. Try-With-Resources, Suppressed & Exception Chaining
Try-With-Resources (Java 7+)
Open Resource
↓
Use Resource
↓
Resource Automatically Closed (finally not required)
Works with AutoCloseable/Closeable: FileInputStream, BufferedReader, Scanner, Connection, PreparedStatement, ResultSet, Socket. Benefits: less boilerplate, no resource leaks, recommended for JDBC.
Suppressed Exception & Exception Chaining
Suppressed Exception → occurs when a primary exception already exists and another exception occurs while closing a resource; it gets attached to the primary exception
Exception Chaining → wraps one exception inside another to preserve the original cause (e.g. SQLException → RepositoryException → ServiceException → Controller)
Why chaining? Preserve root cause, better debugging, layer separation — enterprise standard.
Stack Trace
Sequence of method calls leading to the exception. Read bottom → top; the root cause is usually the last "Caused by".
21. Spring Boot Exception Handling
Global Exception Flow
Client Request
→
Controller
→
Service
→
Repository
→ Exception →
Global Exception Handler
→
JSON Error Response → Client
Local vs Global Exception Handling
| Local (try-catch) | Global (@ControllerAdvice) |
| Inside method, repeated code, hard to maintain | Centralized, reusable, recommended |
@ControllerAdvice vs @RestControllerAdvice
| @ControllerAdvice | @RestControllerAdvice |
| MVC + REST, needs @ResponseBody for JSON | = @ControllerAdvice + @ResponseBody, returns JSON automatically — preferred for REST APIs |
@ResponseStatus vs ResponseStatusException
| @ResponseStatus | ResponseStatusException |
| Static mapping on a custom exception class | Dynamic HTTP status without creating a custom exception class |
@ExceptionHandler — Example Mappings
| UserNotFoundException | 404 |
| DuplicateUserException | 409 |
| IllegalArgumentException / Validation Exception | 400 |
| AccessDeniedException | 403 |
| AuthenticationException | 401 |
| Unhandled Exception | 500 |
ProblemDetail (Spring Boot 3 · RFC 9457) & Standard Error Response
ProblemDetail Fields
type · title · status · detail · instance
Standard, REST-friendly, extensible error format provided by Spring Boot 3.
Recommended Error Response Fields
timestamp · status · error · message · path · traceId · requestId · errors (validation)
22. Validation Exceptions & HTTP Status Cheat Sheet
Validation Flow
@RequestBody
→
@Valid
→
MethodArgumentNotValidException
→
Global Handler → Validation Errors
Common annotations: @NotNull @NotBlank @NotEmpty @Size @Email @Pattern @Positive @Min @Max @Past @Future
| Exception | Occurs When |
| MethodArgumentNotValidException | @RequestBody validation fails |
| ConstraintViolationException | @PathVariable / @RequestParam validation fails |
| BindException | Object binding fails (form data, query params) |
| HttpMessageNotReadableException | Invalid/malformed JSON, wrong data type |
| HttpRequestMethodNotSupportedException | Wrong HTTP method (e.g. GET instead of POST) |
| NoHandlerFoundException | Requested URL not found → 404 |
HTTP Status Cheat Sheet
| 200 | OK |
| 201 | Created |
| 204 | No Content |
| 400 | Bad Request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not Found |
| 405 | Method Not Allowed |
| 409 | Conflict |
| 422 | Unprocessable Entity (optional) |
| 500 | Internal Server Error |
| 503 | Service Unavailable |
Real Project Scenarios
User Registration→Duplicate Email → 409 Conflict
Login→Invalid Credentials → 401 Unauthorized
Certificate Portal→Certificate Not Found → 404
Course Enrollment→Course Full → 400 Bad Request
Payment→Gateway Timeout → 503 Service Unavailable
Database→Constraint Violation → 409 Conflict
23. Best Practices & Anti-Patterns
✓ Best Practices
✓Catch specific exceptions first, most specific → most generic
✓Never catch Throwable; never ignore exceptions
✓Keep try blocks small; always close resources
✓Use try-with-resources for JDBC/file IO
✓Log exceptions properly; log once, handle once
✓Create business-specific exceptions; extend RuntimeException for business errors
✓Preserve root cause via exception chaining
✓Use @RestControllerAdvice + standard ErrorResponse DTO
✓Map correct HTTP status codes; validate input using @Valid
✓Don't expose internal stack trace to clients; include correlation/trace ID
✗ Anti-Patterns
| ✗ Catch Exception / Throwable everywhere |
| ✗ Empty catch block |
| ✗ Printing stack trace in production |
| ✗ Using exceptions for normal control flow |
| ✗ Throwing generic Exception |
| ✗ Swallowing exceptions silently |
| ✗ Returning null instead of throwing an exception |
Logging strategy: Business Exception → WARN, Validation Error → INFO, Technical Exception → ERROR. Never log passwords, OTPs, JWTs, or other sensitive data.
24. Exception Handling — One-Liner Revision
›Throwable → Parent of Errors & Exceptions
›Error → JVM Problem
›Exception → Recoverable Problem
›Checked Exception → Compile Time
›Unchecked Exception → Runtime
›try → Risky Code
›catch → Handle Exception
›finally → Cleanup Block
›Multi-Catch → One catch for multiple exceptions
›throw → Explicitly throw exception
›throws → Declare possible exception
›Propagation → Travels to caller until handled
›Custom Exception → Business-meaningful exception
›Try-With-Resources → Auto resource cleanup
›AutoCloseable → Interface enabling auto-close
›Suppressed Exception → Exception during resource close
›Exception Chaining → Wrap original exception (preserve cause)
›Stack Trace → Method call history to the exception
›@ControllerAdvice → Global exception handler
›@RestControllerAdvice → REST global handler (JSON)
›@ExceptionHandler → Handles a specific exception
›@ResponseStatus → Static HTTP status
›ResponseStatusException → Dynamic HTTP status
›ProblemDetail → Standard Spring Boot 3 error format
›MethodArgumentNotValidException → Request body validation
›ConstraintViolationException → Parameter validation
›AccessDeniedException → 403
›AuthenticationException → 401
›Global Handler → Central error management
›final vs finally vs finalize() → constant / cleanup block / deprecated GC hook
25. Final Combined Revision Flow
15-Minute Multithreading Flow
Fundamentals → Thread Lifecycle
↓
Thread Creation (Runnable/Callable)
↓
Thread Safety (Sync/Volatile/Atomic/CAS)
↓
Locking & Deadlock
↓
Executor Framework & Thread Pools
↓
Concurrent Collections & Sync Utilities
↓
Fork-Join / CompletableFuture / @Async / Kafka
15-Minute Exception Handling Flow
Throwable Hierarchy → Checked/Unchecked
↓
try / catch / finally
↓
throw vs throws → Propagation
↓
Custom Exception → Try-With-Resources
↓
@ControllerAdvice → @ExceptionHandler
↓
ProblemDetail → Validation Exceptions
↓
Logging → Production Best Practices
Interviewers Expect You To Explain
✓ Thread Lifecycle & Internal Working of Executors ✓ Race Conditions & Synchronization Strategies
✓ Deadlock Causes & Prevention ✓ CompletableFuture / Parallel Streams Trade-offs
✓ Checked vs Unchecked Exception Design Choices ✓ Global Exception Handling in Spring Boot
✓ Exception Chaining & Root-Cause Preservation ✓ Real Production Troubleshooting (deadlocks, thread dumps, pool exhaustion)