๐งต 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).
๐ฆ Core Classes:
-
ForkJoinPool
: Specialized thread pool to execute fork/join tasks. -
RecursiveTask<V>
: Used when tasks return a result. -
RecursiveAction
: Used when tasks do not return a result.
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
|
---|
✅ 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."
Thread Management (ExecutorService)
By default, CompletableFuture
uses the ForkJoinPool.commonPool(), but you can provide a custom thread pool:
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 |
Difference between synchronized
and ReentrantLock
✅ 1. synchronized
(Intrinsic Lock)
๐น What is it?
A language-level keyword that provides mutual exclusion using the intrinsic lock (monitor) of an object.
๐ง Syntax:
✅ Features:
-
Simple to use
-
Automatically releases the lock (even on exceptions)
-
Synchronized blocks/methods are reentrant by default
❌ Limitations:
-
No try-lock capability (you must wait)
-
No timeout or interrupt handling
-
Less flexible (can’t check lock status or manage fairness)
✅ 2. ReentrantLock
(Explicit Lock)
๐น What is it?
A class from java.util.concurrent.locks
package that gives fine-grained control over locking.
๐ง Syntax:
✅ Features:
-
Try-locking:
tryLock()
to attempt without blocking -
Timeouts:
tryLock(timeout, unit)
-
Interruptible:
lockInterruptibly()
-
Fairness: Option to grant locks in FIFO order
-
Condition variables: Like
wait()
/notify()
but more powerful
๐ฏ When to Use What?
Use Case | Recommendation |
---|---|
Simple mutual exclusion | synchronized |
Need to try-lock or avoid blocking | ReentrantLock |
Require fairness (FIFO lock order) | ReentrantLock |
Need multiple condition variables | ReentrantLock |
Short critical section, low contention | synchronized |
difference between Runnable
and Callable?
✅ Runnable vs Callable in Java
Feature | Runnable | Callable<V> |
---|---|---|
Package | java.lang | java.util.concurrent |
Return value | No return value (void ) | Returns a result (V ) |
Can throw exception | Cannot throw checked exceptions | Can throw checked exceptions |
Method to override | run() | call() |
Submit via | Thread or ExecutorService | Only ExecutorService |
Used with Future? | No (unless wrapped) | Yes, returns Future<V> |
๐ง Code Example: Runnable
-
Does not return a result
-
Cannot throw checked exceptions
-
Executed via
Thread
orExecutorService
๐ง Code Example: Callable
-
Returns a result
-
Can throw checked exceptions
-
Executed using
ExecutorService
only
๐ง Interview Tip
“Use
Runnable
when no result or exception is needed. UseCallable
when the task needs to return a value or might throw a checked exception. In production-grade concurrent apps,Callable
withExecutorService
andFuture
provides better control.”
✅ Stream vs ParallelStream in Java
Feature | stream() | parallelStream() |
---|---|---|
Execution | Sequential (one thread) | Parallel (multiple threads from ForkJoinPool) |
Performance | Better for small/IO-bound tasks | Better for large/CPU-bound tasks |
Threading | Single-threaded | Multi-threaded (ForkJoinPool.commonPool) |
Ordering | Preserves order by default | May not preserve order |
Suitability | Simpler, more predictable | Requires thread-safety and careful design |
๐ Example: stream()
-
Runs sequentially from start to finish.
-
Easier to debug and reason about.
⚡ Example: parallelStream()
-
Splits data and runs parts concurrently using multiple threads.
-
Faster for large data sets — but only if thread-safe and no shared state.
No comments:
Post a Comment