Friday, February 6, 2026

Multithreading interview questions

  What’s the difference between Concurrency and Parallelism?

  • Concurrency: multiple tasks make progress by interleaving on one or more threads (even on single core).
  • Parallelism: tasks execute simultaneously on multiple cores. In Java, you can write concurrent code without achieving parallel speedup (e.g., due to lock contention, single core, blocking I/O).

 Ways to create a thread in Java?

  1. Extend Thread class
  2. Implement Runnable
  3. Implement Callable + Future
  4. Using Executor framework (ExecutorService)

 Best practice: Use Runnable/Callable with ExecutorService (better separation of concerns).

 Difference between start() and run()?

  • start() → creates a new thread and calls run() internally
  • run() → executes like a normal method (no new thread)

 Calling run() directly does not create a new thread.

 Difference between Runnable and Callable?

Runnable

Callable

No return value

Returns a value

Cannot throw checked exception

Can throw checked exception

run() method

call() method

 What is synchronization?

Synchronization ensures only one thread accesses shared resources at a time, preventing data inconsistency.

synchronized void increment() {

    count++;

}

Method-level vs Block-level synchronization?

  • Method-level: Locks entire method
  • Block-level: Locks specific critical section (better performance)

synchronized(this) {

   // critical section

}

 What is an intrinsic lock / monitor?

Every Java object has an intrinsic lock (monitor).
synchronized uses this lock to control access.

 What is a deadlock?

A situation where two or more threads wait forever for each other’s resources.

Thread A → holds Lock1 → waits for Lock2

Thread B → holds Lock2 → waits for Lock1

 Avoid using:

  • Lock ordering

·        Timeout locks (tryLock)

·        Avoid nested locks

·        Use higher-level concurrency utilities

 Difference between synchronized and ReentrantLock?

synchronized

ReentrantLock

Implicit locking

Explicit locking

No fairness

Supports fairness

No try-lock

tryLock() available

Auto-release

Must unlock manually

 What is volatile?

  • Guarantees visibility, not atomicity
  • Prevents CPU cache inconsistency

volatile boolean running = true;

Not a replacement for synchronization.

 What are Atomic classes?

Classes in java.util.concurrent.atomic that provide lock-free thread-safe operations.

Example:

AtomicInteger count = new AtomicInteger(0);

count.incrementAndGet();

 Thread lifecycle states?

  • NEW
  • RUNNABLE
  • BLOCKED
  • WAITING
  • TIMED_WAITING
  • TERMINATED

 

Difference between wait() and sleep()?

wait()

sleep()

Releases lock

Does NOT release lock

Object method

Thread method

Needs synchronized block

No synchronization needed

 notify() vs notifyAll()?

  • notify() → wakes one waiting thread
  • notifyAll() → wakes all waiting threads

Prefer notifyAll() to avoid thread starvation.

 What is thread starvation?

When a thread never gets CPU time due to priority or lock monopolization.

What is livelock?

Threads are active but cannot make progress (keep responding to each other).

How does Java ensure thread safety in Spring Boot apps?

  • Stateless services
  • Thread-safe beans
  • Proper use of ExecutorService
  • Database-level locking
  • Synchronization when required

Common multithreading bugs you faced?

  • Race conditions
  • Deadlocks
  • Improper synchronization
  • Blocking calls in thread pools
  • Memory visibility issues

 What is the Executor Framework?

The Executor Framework (java.util.concurrent) provides a high-level API for managing threads, separating task submission from task execution.

 Instead of creating threads manually, you submit tasks to an executor.

 Why use Executor instead of new Thread()?

Problems with manual threads:

  • Expensive thread creation
  • No reuse
  • Hard lifecycle management
  • Poor error handling

Executors provide:

  • Thread pooling
  • Task queuing
  • Better performance
  • Graceful shutdown

 What are the core interfaces?

  1. Executor
  2. ExecutorService
  3. ScheduledExecutorService

 Difference between Executor and ExecutorService?

Executor

ExecutorService

Only execute()

submit(), shutdown(), invokeAll()

Fire-and-forget

Full lifecycle management

 What is a Thread Pool?

A set of reusable worker threads that execute submitted tasks from a queue.

Benefits:

  • Reduced overhead
  • Controlled concurrency
  • Better throughput

 Types of Thread Pools in Executors?

FixedThreadPool

CPU-bound tasks

CachedThreadPool

Short-lived async tasks

SingleThreadExecutor

Sequential execution

ScheduledThreadPool

Delayed / periodic tasks

ForkJoinPool

Divide-and-conquer

 Difference: execute() vs submit()?

execute()

submit()

No return

Returns Future

Exceptions uncaught

Exceptions captured

 What is a Future?

Represents the result of an asynchronous computation.

 Future<Integer> f = executor.submit(task);

f.get(); // blocks

 Difference between Future.get() and CompletableFuture?

  • Future.get() blocks
  • CompletableFuture supports non-blocking callbacks and chaining

What is Callable?

Similar to Runnable but:

  • Returns a value
  • Can throw checked exceptions 

What happens if a task throws an exception?

  • execute() → exception lost
  • submit() → captured inside Future

Must call get() to see it.

 How does ThreadPoolExecutor work internally?

  1. Use core threads
  2. Queue tasks
  3. Create extra threads if queue is full
  4. Reject if max threads reached

What is ForkJoinPool?

Designed for divide-and-conquer tasks using work-stealing algorithm.

Used in:

  • Parallel Streams
  • RecursiveTask / RecursiveAction 

Why CopyOnWriteArrayList?

  • Thread-safe without locking during reads
  • Ideal for read-heavy scenarios 

What is CompletableFuture?

CompletableFuture is a Java concurrency API (Java 8+) that represents the result of an asynchronous, non-blocking computation and allows you to chain, combine, and react to tasks without blocking threads.

Think of it as:

A Future + callbacks + functional style + composition

 Why was CompletableFuture introduced?

Problems with Future:

  • get() blocks
  • No callbacks
  • No easy task chaining
  • Poor error handling

CompletableFuture solves all of these.

 How CompletableFuture Works (Conceptually)

  1. A task runs asynchronously in a thread pool
  2. It produces a result (or exception)
  3. Dependent stages are triggered automatically
  4. Threads are not blocked while waiting

 Ex:

CompletableFuture<String> future =

    CompletableFuture.supplyAsync(() -> {

        return "Hello";

    });

 System.out.println(future.get());

 

supplyAsync() → runs task asynchronously

Uses ForkJoinPool.commonPool() by default

get() blocks only at the end

 

Execution Flow (Interview Explanation)

Main Thread

   |

   |---- submit async task

   |

   |---- continues execution

              |

              |---- worker thread completes task

              |

              |---- result is set inside CompletableFuture

 

 runAsync() vs supplyAsync()

runAsync()

CompletableFuture<Void>

supplyAsync()

CompletableFuture<T>

 

Chaining – thenApply

Transforms result.

CompletableFuture<Integer> future =

    CompletableFuture.supplyAsync(() -> 10)

        .thenApply(x -> x * 2);

Runs after previous stage completes

 

Consuming Result – thenAccept

CompletableFuture<Void> future =

    CompletableFuture.supplyAsync(() -> "Data")

        .thenAccept(data -> System.out.println(data));

No return value

 

Running Without Input – thenRun

future.thenRun(() -> System.out.println("Done"));

 

Async vs Non-Async Variants (Very Tricky)

thenApply()       // runs in same thread

thenApplyAsync()  // runs in another thread pool


“Non-Async continues in the same thread that completed the previous stage.”

 

 

 

 

 

 

No comments:

Post a Comment