๐งต 1. Core Concepts of Multithreading
Concept | Explanation |
---|---|
Thread | Smallest unit of execution; runs independently. |
Runnable / Callable | Represent units of work (Callable returns a result). |
Thread Safety | Multiple threads safely accessing shared data (e.g., using synchronized , locks , atomics ). |
Race Condition | Occurs when threads access shared data without proper synchronization. |
Deadlock | Two or more threads waiting on each other to release locks. |
⚙️ 2. Executors Framework
Introduced in Java 5 to manage thread creation and pooling efficiently.
Executors (Thread Pools)
๐น Why Use Executors?
-
Creating threads manually is costly and inefficient.
-
Executors manage a pool of threads.
-
Avoids thread exhaustion & allows task reuse.
๐น Types of Executors
Type | Best Use Case |
---|---|
newFixedThreadPool(n) | Predictable concurrency, limited threads |
newCachedThreadPool() | Burst loads, short-lived tasks |
newSingleThreadExecutor() | Sequential task execution |
newScheduledThreadPool(n) | Scheduled/periodic tasks |
✅ Interview Answer: “We used a fixed thread pool for CPU-bound tasks to avoid excessive context switching.”
๐ง 3. ForkJoinPool (Java 7+)
Used for parallelism—divides tasks into smaller subtasks (divide-and-conquer).
Used for parallelism — especially in recursive tasks like:
-
Sorting
-
File searches
-
Data crunching
๐น Based on work stealing algorithm:
-
Idle threads steal tasks from busy ones
Feature | Benefit |
---|---|
Work-stealing | Idle threads can "steal" tasks from others—boosts efficiency. |
RecursiveTask / RecursiveAction | Handles subtasks with/without results. |
✅ Interview Answer: “We parallelized complex data transformations with ForkJoinPool, which significantly reduced latency due to work-stealing.”
๐ฎ 4. CompletableFuture (Java 8+)
Asynchronous programming without blocking threads.
Introduced in Java 8 for async programming.
-
Chain operations with callbacks (
thenApply
,thenAccept
) -
Combine multiple futures
-
Handle exceptions (
exceptionally
,handle
)
๐ Use Cases
-
Async I/O and microservice orchestration
-
Parallel API calls
-
Composing dependent tasks without blocking
Method | Purpose |
---|---|
supplyAsync() | Start async task with result |
thenApply() | Transform result |
thenCombine() | Combine results of two futures |
exceptionally() | Handle errors gracefully |
✅ Interview Answer: “We used
CompletableFuture
to orchestrate three async microservice calls and combine their results withthenCombine
, reducing response time from 2s to under 800ms.”
⚠️ 5. Best Practices
Practice | Benefit |
---|---|
Use Executors, not raw Thread | Scalable, better resource control |
Use Thread-safe collections | Avoid ConcurrentModificationException |
Prefer immutable objects | Reduce need for synchronization |
Monitor thread pools | Detect bottlenecks (ThreadPoolExecutor#getActiveCount ) |
Avoid blocking in async flows | Use thenCompose instead of join() |
✅ Pro Interview Template
"I chose
CompletableFuture
because we needed non-blocking orchestration of three APIs. It fits well into our microservices architecture and allows us to write readable, async code usingthenApply
andthenCombine
, instead of managing threads manually. This keeps our service responsive and scalable."
Best Practices
Practice | Why it Matters |
---|---|
Use ExecutorService | Manages thread pooling safely |
Always shutdown() executors | Prevent resource leaks |
Prefer Callable over Runnable | Allows exception handling and result return |
Use CompletableFuture for async chains | Cleaner and non-blocking code |
Use ForkJoinPool for recursive divide-and-conquer | Better performance for large tasks |
⚠️ Common Pitfalls
Pitfall | Explanation |
---|---|
Blocking on .get() in main thread | Kills async benefit; prefer then* chains |
Forgetting to shutdown executors | Causes app to hang |
Using too many threads | Thread contention, memory pressure |
Not handling exceptions in async tasks | Can silently fail or break chains |
No comments:
Post a Comment