Article written by Shashi Kadapa, under the guidance of Thomas Gilmour, Ex-LinkedIn and PayPal leader turned engineering coach, mentoring 100+ engineers into FAANG+ roles. Reviewed by Manish Chawla, a problem-solver, ML enthusiast, and an Engineering Leader with 20+ years of experience.
Java multithreading interview questions are critical for software engineers. Multithreading helps a program run several parts or threads concurrently. A thread is a light unit of execution that shares memory with other process threads, but runs independently.
Multithreading interview questions in Java are on the advantages. The benefits are efficient CPU utilization, improved responsiveness, handling multiple web services, running background tasks, parallel execution, and context switching speed.
You will be asked to code during the Java multithreading interview questions. This blog presents questions and answers on Java multithreading, thread creation and execution, Java synchronization, thread communication, and other important multithreading interview questions in Java.
The blog will guide candidates, answer Java multithreading interview questions, and help developers who want to enhance their knowledge of Java.
Java multithreading helps several threads, which are small units of a program, to run at the same time in a single process. It is used in parallel processing, background operations, and managing multiple users simultaneously. Threads are created with the Thread class or by implementing the Runnable interface.
Multithreading matters since application performance is enhanced when several threads run simultaneously and not one after another. Responsiveness increases, and CPU resource usage is enhanced. The following figure presents a block diagram of a Java process with three threads. You are asked to draw and explain such diagrams in Java multithreading interview questions.
The heap has all object instances and is accessible by the threads. The method area has the static fields, class metadata, and constants, and shared access makes synchronization essential.
Each thread has a program counter that tracks instructions, a stack to hold variables and call frames for method invocation, and native JNI/ OS calls. Thread states are actively executing, waiting to secure a monitor lock.

This section presents commonly asked Java multithreading interview questions and answers.
Multithreading in Java is the functionality to run multiple threads or tasks concurrently in a single program. An analogy is of a cook preparing several dishes at the same time rather than doing them one after another.
A thread in Java is the smallest discrete unit that runs independently in a program. It allows the application to carry out several tasks in the same process.
A process is an independent program that has a memory space. Tread is an execution in a process and shares memory with other threads.
The following table gives a comparison of processes and threads for key aspects.
| Aspect | Process | Thread |
|---|---|---|
| Memory | Has its own separate memory space | Shares memory within the same process |
| Overhead | High with creation and context switching costly | Low, lightweight and faster switching |
| Dependency | Independent of other processes | Dependent on the parent process |
| Communication | Uses IPC such as pipes, and sockets | Uses shared memory with faster communication |
| Real-world Example | Running different apps such as Chrome, VS Code | Multiple tabs in a browser or tasks in an app |
A thread has different states in its lifecycle, which are part of the Thread.State enumeration. The states are NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, AND Simple Lifecycle Flow.
A simple life cycle state of a thread is given below.
NEW
│
▼
RUNNABLE ────────────────► TERMINATED
│ ▲
│ │
▼ │
BLOCKED / WAITING / TIMED_WAITING
│
└──────────────► (back to RUNNABLE)
Threads are created in two ways. The first method involves creating a subclass that extends the Thread class and overrides the run() method. The second method that is often preferred is to define a class to implement the Runnable interface and its run() method.
start() is to create a new thread and calls run() internally. The class run() performs as a standard method in the current thread. The following table compares the two.
| Feature | start() | run() |
|---|---|---|
| Purpose | Starts a new thread | Contains thread logic |
| Thread Creation | Yes and new thread created) | No as it runs in current thread |
| Execution | Asynchronous or parallel | Synchronous or sequential |
| Call Behavior | Calls run() internally | Called like a normal method |
| Multiple Calls | Cannot call twice as it gives error | Can call multiple times |
| Performance | Allows multithreading | No multithreading |
Multithreading has multiple threads running simultaneously in a program. Benefits are:
The user thread is the main thread that runs an application. Daemon thread runs in the background and supports user threads.
The following table compares a user thread and a daemon thread.
| Feature | User Thread | Daemon Thread |
|---|---|---|
| Purpose | Runs main tasks | Supports background tasks |
| JVM Behavior | JVM waits for it to finish | JVM does not wait and stops automatically |
| Lifespan | Runs to task completion | Ends with user threads ending |
| Examples | Main thread, worker threads | Garbage Collector |
| Priority | Higher importance | Lower importance |
A daemon thread is created by calling the setDaemon(true) function before starting the thread, but an error appears.
The following code shows the creation of a daemon thread.
class MyDaemon extends Thread {
public void run() {
while (true) {
System.out.println("Daemon thread running...");
}
}
}
public class Main {
public static void main(String[] args) {
MyDaemon t = new MyDaemon();
t.setDaemon(true); // Set as daemon thread
t.start(); // Start the thread
System.out.println("Main thread ends");
}
}
Thread priority is a value from 1 to 10 to show thread importance, and it helps the CPU scheduler to decide which thread to run first. Code for the thread priority is:
Thread t1 = new Thread();
t1.setPriority(Thread.MAX_PRIORITY); // 10
No, you cannot start a thread twice. This is a common trap question in multithreading interview questions in Java.
The reason why you cannot start a thread twice in Java is that the start() method is called, it moves from NEW to RUNNABLE to TERMINATED. If you call the start() again after termination on the same thread, a IllegalThreadStateException is thrown.
If you call run() directly, then a new thread is not created, and it runs as a normal method in the current thread. This is a common trap question in multithreading interview questions in Java.
The reason is start() creates a new thread and then calls run() internally, and multithreading occurs. run() executes in the same thread without concurrency.
The code snippet is:
Thread t = new Thread(() -> {
System.out.println("Running in thread");
});
t.run(); // Runs in main thread
t.start(); // Runs in new thread.
A Java application begins with one main thread that uses the main() method. Additional threads can be created and run from this thread.
A thread is created by extending the thread class and overriding its run() method. Java interview questions on multithreading are on this topic.
Code example of creating the thread with the thread class is:
// Step 1: Create a class that extends Thread
class MyThread extends Thread {
// Step 2: Override the run() method
public void run() {
// Task that the thread will perform
for (int i = 1; i <= 5; i++) {
System.out.println("Child Thread: " + i);
try {
Thread.sleep(500); // pause for 500ms
} catch (InterruptedException e) {
System.out.println("Thread interrupted");
}
}
}
}
// Main class
public class ThreadExample {
public static void main(String[] args) {
// Step 3: Create thread object
MyThread t1 = new MyThread();
// Step 4: Start the thread
t1.start();
// Main thread work
for (int i = 1; i <= 5; i++) {
System.out.println("Main Thread: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
System.out.println("Main thread interrupted");
}
}
}
}
// Output:
// Main Thread: 1
// Child Thread: 1
// Main Thread: 2
// Child Thread: 2
Explanation of code for creating the thread with the thread class is given below. Java multithreading interview questions ask you to explain the code.
The Runnable interface is the best way to create threads as it allows better flexibility, and it is also possible to extend another class. Java multithreading interview questions are on this topic.
Code sample to create threads with the Runnable interface is:
// Step 1: Create a class that implements Runnable
class MyRunnable implements Runnable {
// Step 2: Override the run() method
public void run() {
// Task that the thread will perform
for (int i = 1; i <= 5; i++) {
System.out.println("Child Thread: " + i);
try {
Thread.sleep(500); // pause for 500ms
} catch (InterruptedException e) {
System.out.println("Thread interrupted");
}
}
}
}
// Main class
public class RunnableExample {
public static void main(String[] args) {
// Step 3: Create Runnable object
MyRunnable r1 = new MyRunnable();
// Step 4: Pass Runnable object to Thread
Thread t1 = new Thread(r1);
// Step 5: Start the thread
t1.start();
// Main thread work
for (int i = 1; i <= 5; i++) {
System.out.println("Main Thread: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
System.out.println("Main thread interrupted");
}
}
}
}
// Output (Example)
// Main Thread: 1
// Child Thread: 1
// Main Thread: 2
// Child Thread: 2
Explanation of the steps of the code to create threads with the Runnable interface is:
While Runnable and Callable define tasks to run in a separate thread, they have some differences. Runnable does not return a result and cannot give checked exceptions. Callable returns a result and gives checked exceptions.
The following table compares Runnable and Callable.
| Feature | Runnable | Callable |
|---|---|---|
| Package | java.lang | java.util.concurrent |
| Method | run() | call() |
| Return Type | void (no result) | Returns a value (V) |
| Exception Handling | Cannot throw checked exceptions | Can throw checked exceptions |
| Introduced In | Java 1.0 | Java 5 |
| Use with Executor | ExecutorService.execute() | ExecutorService.submit() |
| Result Retrieval | Not possible | Possible using Future |
| Functional Interface | Yes, use lambda) | Yes, use lambda |
Runnable is preferred over extending the thread class since it is a better design choice. Runnable supports multiple inheritance, provides separation of concerns, leading to cleaner and maintainable code. Runnable code is reusable, and multiple threads can use the runnable object. It fits modern Java and the Executor Framework, and it is flexible. It is frequently asked multithreading interview questions in Java.
If you create a thread but do not override the run() method, then nothing is executed by the thread. The reason is that the thread class already has a default run() method, and the default implementation is empty, doing nothing. This is a common trap in Java multithreading interview questions.
Also Read: 100+ Java Algorithms Interview Questions for All Experience Levels
Thread safety is a common topic in Java multithreading interview questions. Thread safety helps a method or class to perform as required when multiple threads are run. If it is not present, then shared data concurrent access can lead to race conditions.
In race conditions, the result depends on whether you are forced to rely on unpredictable thread timing causing data corruption. Multithreading in Java interview questions focus on several aspects of thread safety and synchronization.
Synchronization mechanisms control access to shared resources in a multithreaded environment. It allows only one thread to access a critical section of code at a time. The process prevents race conditions and resulting data inconsistency.
Code snippet with a synchronized method is given below:
class Counter {
private int count = 0;
// synchronized method
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
class MyThread extends Thread {
Counter counter;
MyThread(Counter counter) {
this.counter = counter;
}
public void run() {
for (int i = 0; i < 1000; i++) {
counter.increment(); // shared resource
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
MyThread t1 = new MyThread(counter);
MyThread t2 = new MyThread(counter);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Final Count: " + counter.getCount());
}
}
A race condition happens when several threads simultaneously access and modify shared data at the same time. The program result relies on unpredictable timing and the order of thread execution.
Code snippet of a race condition without Synchronization
class Counter {
int count = 0;
// NOT synchronized
public void increment() {
count++; // not atomic (read → modify → write)
}
}
class MyThread extends Thread {
Counter counter;
MyThread(Counter counter) {
this.counter = counter;
}
public void run() {
for (int i = 0; i < 1000; i++) {
The synchronized method is used to make the whole method thread-safe. A synchronized block is used for better performance and control.
Code for synchronized method:
class Counter {
int count = 0;
// NOT synchronized
public void increment() {
count++; // not atomic (read → modify → write)
}
}
class MyThread extends Thread {
Counter counter;
MyThread(Counter counter) {
this.counter = counter;
}
public void run() {
for (int i = 0; i < 1000; i++) {
The following table compares the synchronized method and synchronized block.
| Feature | Synchronized Method | Synchronized Block |
|---|---|---|
| Definition | Entire method is locked | Only a specific block of code is locked |
| Syntax | public synchronized void method() | synchronized(object) { … } |
| Lock Scope | Whole method | Only the critical section |
| Lock Object | Implicit (this for instance, class for static) | Explicit (you choose the object) |
| Performance | Slower (locks full method) | Faster (locks only required part) |
| Flexibility | Less flexible | More flexible |
| Granularity | Coarse-grained locking | Fine-grained locking |
| Control | No control over lock object | Full control over which object to lock |
| Use Case | When entire method needs synchronization | When only part of method needs synchronization |
Object-Level Lock synchronizes blocks or non-static methods. Only one thread can access a synchronized code instance. Class-Level Lock manages static data, allowing a single thread to execute a static synchronized block for all instances of that class.
The following table compares Class-Level Lock and Object-Level Lock.
| Feature | Object-Level Lock (Instance Lock) | Class-Level Lock (Static Lock) |
|---|---|---|
| Lock Applied On | Specific object (instance) | Entire class (ClassName.class) |
| Scope of Lock | Limited to one object | Shared across all objects |
| Method Type | synchronized instance method | static synchronized method |
| Number of Locks | One lock per object | Only one lock per class |
| Parallel Execution | Allowed (different objects) | Not allowed (even with multiple objects) |
| Memory Association | Heap (object-level) | Method Area / Class metadata |
| Blocking Behavior | Threads block only for same object | Threads block across all instances |
| Performance | Better (more concurrency possible) | Lower (more contention) |
| Use Case | Instance-specific data protection | Shared/static data protection |
| Example Lock | this | ClassName.class |
The volatile keyword allows visibility of changes to variables across threads. Each thread obtains updated values from the main memory. The volatile keyword stops variable caching in thread value memory, allowing visibility and stopping atomicity. Java multithreading interview questions are on this topic.
Code snippet for volatile keywords is:
class SharedFlag {
boolean running = true;
}
public class Main {
public static void main(String[] args) {
SharedFlag flag = new SharedFlag();
Thread t1 = new Thread(() -> {
while (flag.running) {
// Infinite loop (may never stop)
}
System.out.println("Stopped");
});
Thread t2 = new Thread(() -> {
try { Thread.sleep(1000); } catch (Exception e) {}
flag.running = false;
System.out.println("Flag changed to false");
});
t1.start();
t2.start();
}
}
While volatile and synchronized are used in Java for multithreading to handle data, they serve different purposes. Volatile gives visibility of variable changes over several threads. Synchronization provides properties of mutual exclusion, visibility, and atomicity.
This is a common topic of multithreading in Java interview questions. The following table compares volatile and synchronized in Java.
| Feature | volatile | synchronized |
|---|---|---|
| Purpose | Visibility of changes | Control access (mutual exclusion) |
| Visibility Guarantee | Yes | Yes |
| Atomicity | No | Yes |
| Thread Safety | Partial | Full (for critical section) |
| Locking Mechanism | No locking | Uses intrinsic lock (monitor) |
| Performance | Faster (no lock overhead) | Slower (due to locking) |
| Scope | Variables only | Methods / blocks |
| Blocking | Non-blocking | Blocking (threads may wait) |
| Use Case | Flags, simple state variables | Complex operations, shared resources |
| Race Condition Safe | No | Yes |
Java Memory Model (JMM) explains the process of thread interaction with memory. It specifies the method of variable storage, read, and update between thread-local and main memory.
JMM lays down the shared data rules for visibility, ordering, and consistency. Multithreading interview questions in Java are on JMM.
The happens-before relationship is from the Java Memory Model. It specified that an action’s result is visible to another and runs in a specific order. It removes outdated values, prevents out-of-order execution, and gives consistent output.
Multithreading in Java interview questions are on this subject. Code snippet for happens-before relationships in Java is:
class SharedData {
int value = 0;
volatile boolean flag = false;
}
public class Main {
public static void main(String[] args) {
SharedData data = new SharedData();
Thread writer = new Thread(() -> {
data.value = 42; // (1)
data.flag = true; // (2) happens-before
});
Thread reader = new Thread(() -> {
if (data.flag) { // (3)
System.out.println(data.value); // (4)
}
});
writer.start();
reader.start();
}
}
Thread safety ensures that the code or a class behaves as required when it is accessed by multiple threads simultaneously. It stops race conditions and data inconsistency. Java interview questions on multithreading are frequently on thread safety.
The ways to achieve thread safety are by using synchronized, volatile, atomic classes, locks, immutable objects, thread local variables, and concurrent collections.
Lock interface is available in java.util.concurrent.locks, and is a flexible and powerful means to control thread synchronization in comparison to synchronized. One of the common Java multithreading interview questions permits explicit unlocking and locking with a common implementation using the ReentrantLock class.
Also Read: Advanced Java Interview Questions and Answers for Coding Interview
Thread communication and coordination is the process for multiple threads to work, share data safely, and coordinate execution order together. Java multithreading interview questions commonly focus on thread communication and coordination.
Object classes wait(), notify(), and notifyAll() carry out inter-thread communication. They make threads to coordinate execution in shared resources.
Code snippet for wait() method is:
// wait()
synchronized (obj) {
obj.wait(); // releases lock and waits
}
// notify()
synchronized (obj) {
obj.notify(); // wakes up one waiting thread
}
// notifyAll()
synchronized (obj) {
obj.notifyAll(); // wakes all waiting threads
}
Methods wait() and sleep() help to pause thread execution. However, they have differences in locking and synchronization. This is a common trap question in core Java multithreading interview questions.
The following table compares wait() and sleep().
| Feature | wait() | notify() | notifyAll() |
|---|---|---|---|
| Purpose | Pause thread | Wake one thread | Wake all threads |
| Releases lock | Yes | No | No |
| Threads affected | Current thread | One waiting thread | All waiting threads |
| Usage | Inside synchronized | Inside synchronized | Inside synchronized |
wait() and sleep() methods pause thread execution, but they are different in locking and synchronization. wait() is used for thread coordination, and releases the lock and waits for notification. sleep() is used for timing delays, and it keeps the lock and waits for the time. This is a common trap question in core Java multithreading interview questions.
A common trap question in core Java multithreading interview questions is notify(), which wakes one thread and is used when only one thread must be run. notifyAll() wakes all threads, and is used when multiple threads have to proceed.
Threads communicate with shared objects and coordination methods such as wait(), notify(), and notifyAll(). They pause a thread and instruct when the work is ready.
Multithreading in Java interview questions are on this topic. Code snippet of how threads communicate with each other in Java is:
class SharedResource {
private int data;
private boolean hasData = false;
public synchronized void produce(int value) throws InterruptedException {
while (hasData) {
wait(); // wait until data is consumed
}
data = value;
System.out.println("Produced: " + value);
hasData = true;
notify(); // notify consumer
}
public synchronized void consume() throws InterruptedException {
while (!hasData) {
wait(); // wait until data is produced
}
System.out.println("Consumed: " + data);
hasData = false;
notify(); // notify producer
}
}
public class Main {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
Thread producer = new Thread(() -> {
try {
for (int i = 1; i <= 5; i++) {
resource.produce(i);
}
} catch (InterruptedException e) {}
});
Thread consumer = new Thread(() -> {
try {
for (int i = 1; i <= 5; i++) {
resource.consume();
}
} catch (InterruptedException e) {}
});
producer.start();
consumer.start();
}
}
The join() method pauses the current thread until another thread completes its execution. Execution order and sequence between threads is maintained, and it is implemented for dependency handling.
Multithreading interview questions in Java are on coding with the join() method. A code snippet for the join() method is:
class Example {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 1; i <= 3; i++) {
System.out.println("Thread t1: " + i);
try { Thread.sleep(500); } catch (Exception e) {}
}
});
Thread t2 = new Thread(() -> {
System.out.println("Thread t2 starts after t1 finishes");
});
t1.start();
t1.join(); // main thread waits for t1 to finish
t2.start();
}
}
Context switching process managed by the OS scheduler, where the CPU pauses one thread and switches to another. The execution state is saved and restored. It helps in concurrency and multitasking.
Managed by JVM/OS, the thread scheduler selects the thread that accesses CPU time at any moment. Time slicing is a scheduling technique where threads receive a quantum or a time slot to execute before the CPU switches to another thread. Multithreading interview questions in Java are frequently on this topic.
Java has several sets of utilities for easier multithreading and to avoid low-level thread handling. The Java multithreading interview questions in this section are on Java concurrency utilities and the thread pool.
A thread pool in Java is a collection of pre-created threads that are reused to execute multiple tasks, instead of creating a new thread every time.
An analogy is preparing common gravy and using the same gravy for different dishes, adding the appropriate ingredients to make them taste different.
ExecutorService is a high-level interface from java.util.concurrent that separates task submission from thread management. It manages a thread pool and handles task execution. Stability and performance are improved, and it runs asynchronous tasks.
Java multithreading interview questions are on writing the code for ExecutorService. A code example of ExecutorService is:
import java.util.concurrent.*;
public class ExecutorServiceExample {
public static void main(String[] args) {
// Create a thread pool with 2 threads
ExecutorService executor = Executors.newFixedThreadPool(2);
// Submit tasks
for (int i = 1; i <= 3; i++) {
int taskId = i;
executor.submit(() -> {
System.out.println(
"Executing Task " + taskId +
" by " + Thread.currentThread().getName()
);
});
}
// Shutdown the executor
executor.shutdown();
}
}
ConcurrentHashMap is a scalable and fast class used for full synchronization. It has fine-grained locking and is better than Hashtable which is older and slower. Java multithreading interview questions are on comparisons and use cases of the two classes.
The following table compares ConcurrentHashMap and Hashtable.
| Feature | ConcurrentHashMap | Hashtable |
|---|---|---|
| Synchronization | Uses segment-level / bucket-level locking (fine-grained) | Uses method-level synchronization (coarse-grained) |
| Performance | Faster and allows multiple threads simultaneously | Slower as only one thread runs at a time. |
| Thread Safety | Yes . It is highly scalable) | Yes, but is less efficient |
| Null Keys/Values | Not allowed | Not allowed |
| Locking Mechanism | Locks only a portion of the map | Locks entire map |
| Iteration | Weakly consistent and does not give ConcurrentModificationException) | Fail-fast and may throw exceptions. |
| Scalability | High. It is better for concurrent apps) | Low |
| Legacy | Modern and it was introduced in Java 1.5 | Legacy class and has older design |
| Usage Recommendation | Preferred in multithreading | Rarely used now |
The Fork/Join Framework belongs to java.util.concurrent, and efficiently uses several CPU cores. It is used for parallel processing and splits a large task into smaller subtasks called forks. Forks are processed concurrently, and then the results are joined.
Java concurrency and multithreading interview questions are on this topic. Analogy is servicing a large truck. A single mechanic takes more time. When multiple mechanics service different areas at the same time, the work is much faster.
BlockingQueue from java.util.concurrent is a thread-safe queue to automatically manage synchronization between threads. It blocks the producer thread when the queue is full, and the consumer thread when the queue is empty. Java multithreading interview questions are on BlockingQueue.
The two main types of BlockingQueue are Bounded BlockingQueue, and Unbounded BlockingQueue. The Bounded BlockingQueue queue with a fixed capacity blocks when it is full. An example is ArrayBlockingQueue. The Unbounded BlockingQueue does not have a fixed limit and rarely blocks on insert. An example is LinkedBlockingQueue.
Frequently asked in Java multithreading interview questions, CountDownLatch available in java.util.concurrent, is a synchronization utility. It permits threads to wait for the sets of operations to be completed.
CountDownLatch has a counter, and threads call countDown() to reduce the count, and waiting threads call await() to wait until the count becomes 0.
Code snippet for CountDownLatch is:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
Runnable task = () -> {
System.out.println(
Thread.currentThread().getName() +
" completed work"
);
latch.countDown(); // reduce count
};
new Thread(task).start();
new Thread(task).start();
new Thread(task).start();
latch.await(); // main thread waits
System.out.println(
"All tasks finished. Main thread continues."
);
}
}
CyclicBarrier in java.util.concurrent is a synchronization utility. It permits a thread group to wait for each other at a common barrier point before continuing. They are released together after they reach the barrier.
CountDownLatch waits for threads to finish while others finish. A table of comparison of CyclicBarrier and CountDownLatch is given below.
| Feature | CyclicBarrier | CountDownLatch |
|---|---|---|
| Purpose | Threads wait for each other | One/more threads wait for others to finish |
| Counter Reset | It can be reused cyclic | It has a one-time use |
| Who Waits? | All participating threads | Usually main thread waits |
| Method Used | await() | await() + countDown() |
| Trigger Condition | All threads reach barrier | Count reaches zero |
| Reusability | Yes | No |
| Use Case | Parallel tasks meeting at a point | Waiting for tasks to complete |
A Semaphore from java.util.concurrent is a synchronization tool to allow only a fixed number of threads to access a resource at the same time. Permits control access to shared resources. acquire() obtains a permit and waits if it is not available, and release() returns a permit.
Java multithreading interview questions ask you to give a real- world example, such as: A hotel has 10 rooms and only 10 guests can stay at a time. When more guests arrive, they have to wait until a room is vacant.
The CompletableFuture class helps in asynchronous programming. It runs tasks in the background, chains multiple tasks, and manages results without blocking. It supports non-blocking execution, task chaining with thenApply and thenAccept, and combines several tasks.
Multithreading interview questions in Java will be on coding CompletableFuture. A sample code for CompletableFuture is:
import java.util.concurrent.CompletableFuture;
public class CompletableFutureExample {
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> {
return "Hello";
}).thenApply(result -> {
return result + " World";
}).thenAccept(finalResult -> {
System.out.println(finalResult);
});
// Prevent main from exiting immediately (for demo)
try { Thread.sleep(1000); } catch (Exception e) {}
}
}
Also Read: Top Java Concurrency Interview Questions and Answers
Senior developers with 5-8 years’ experience face advanced Java multithreading interview questions. Advanced multithreading interview questions in Java are on starvation, Livelock, AtomicInteger, ReentrantLock, ReadWriteLock, Busy Spinning, and Shutdown Hook.
Deadlock happens in Java programs when multiple threads are permanently blocked because a thread waits for a resource held by another thread. The problems occur due to improper lock handling, mutual exclusion, hold and wait, no preemption, and circular wait.
Core Java multithreading interview questions are on coding for deadlock. AN example code for deadlock is:
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;
public class TryLockExample {
private static final ReentrantLock lock1 = new ReentrantLock();
private static final ReentrantLock lock2 = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
if (lock1.tryLock(1, TimeUnit.SECONDS)) {
System.out.println("Thread 1 acquired lock1");
if (lock2.tryLock(1, TimeUnit.SECONDS)) {
System.out.println("Thread 1 acquired lock2");
lock2.unlock();
}
lock1.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
}
}
Thread starvation occurs when a thread cannot access CPU time or resources for a long period, since other threads keep using the resources. The thread is not deadlocked or blocked, but ignored.
Multithreading in Java interview questions ask you to explain thread starvation with an analogy. An example is: During a banquet, the table of food is surrounded by VIPs or high-priority guests who keep on eating. Other guests of low-priority threads never reach the table.
Livelock starts when multiple threads are not blocked, but they continue to respond to each other and change state, without any progress. The threads are active but stuck in a loop.
An analogy is: Two people are at the elevator door, each moving to the same side to let the other pass, left-right continues, but nothing happens.
The ThreadLocal class gives thread-local variables, where each thread gets an independent copy of a variable. Even when multiple threads use the same ThreadLocal object, the value is not shared.
It is used to avoid synchronization and stop data interference between threads. It is used for per-thread data such as user sessions, database connections, and date formatting. Code snippet for ThreadLocal is:
public class ThreadLocalExample {
private static ThreadLocal<Integer> threadLocal =
ThreadLocal.withInitial(() -> 0);
public static void main(String[] args) {
Runnable task = () -> {
// Each thread sets its own value
threadLocal.set((int) (Math.random() * 100));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Each thread gets its own value
System.out.println(
Thread.currentThread().getName() +
" value: " + threadLocal.get()
);
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
}
}
// Output:
// Thread-0 value: 42
// Thread-1 value: 87
The AtomicInteger class from the java.util.concurrent.atomic package provides thread-safe operations on an integer without synchronization. It uses thread-safe programming and lock-free, based on CAS – Compare-And-Swap. Several threads update the value without blocking each other. It offers fast, thread-safe integer updates without locks and is used for counters in concurrent applications.
You are asked to write the code in Java concurrency and multithreading interview questions for AtomicInteger use. Sample code snippet is:
import java.util.concurrent.atomic.AtomicInteger;
class Counter {
AtomicInteger count = new AtomicInteger(0);
void increment() {
count.incrementAndGet(); // thread-safe
}
}
public class AtomicExample {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(
"Final Count: " + counter.count.get()
);
}
}
ReentrantLock class from java.util.concurrent.locks allows explicit, flexible locking for thread synchronization. A thread with the lock can acquire it again without getting blocked. Synchronized is used for simple locking; advanced features are not needed, and code readability is more important than flexibility.
The following table compares ReentrantLock and synchronized.
| Feature | ReentrantLock | synchronized |
|---|---|---|
| Lock type | Explicit and manual | Implicit and JVM-managed) |
| Lock/unlock control | lock() / unlock() | Automatic |
| Fairness | Can be fair with new ReentrantLock(true)) | No fairness guarantee |
| Try without blocking | TryLock() | Not possible |
| Interruptible lock | lockInterruptibly() | Not supported |
| Multiple conditions | Condition objects | Only one with wait/notify |
| Performance | Better under high contention | Simpler, sometimes slower |
| Risk | Must manually release with risk of deadlock | Safer with auto release |
| Flexibility | High | Low |
| Readability | Slightly complex | Simple |
ReadWriteLock allows separate locks for reading and writing. Performance is improved by allowing multiple threads to read simultaneously. It is used in the implementation of ReentrantReadWriteLock.
Multithreading interview questions in Java on ReadWriteLock ask you to give a real-world use case. An example is: An example is a library system boot catalog allowing thousands of users to search and read books. When the admin runs updates, read/ write is not allowed.
Busy spinning happens when a thread continuously checks a loop condition without releasing the CPU, instead of waiting or sleeping. The thread does not block but keeps running and consuming CPU cycles until the condition becomes true.
The shutdown hook thread is run by the JVM just before the program terminates. It is used for cleanup tasks such as closing files, releasing resources, saving state, and logging shutdown info. It runs when the program finishes normally, System.exit() is called, and when the JVM is terminated.
Code snippet for Shutdown Hook is:
public class ShutdownHookExample {
public static void main(String[] args) {
// Register shutdown hook
Runtime.getRuntime().addShutdownHook(
new Thread(() -> {
System.out.println("Shutdown hook executed!");
// cleanup code here
})
);
System.out.println("Application is running...");
// Simulate some work
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Application ending...");
}
}
Java 21, released in 2023, is a major version that provides many advancements and is currently used in all the latest Java applications. Java multithreading interview questions will be on Java 21 concurrency, virtual threads, and modern APIs.

Virtual threads are created by the JVM, not the OS. They are lightweight, and millions can be created, unlike traditional platforms. Unlike traditional platform threads, you can create millions of them.
Virtual threads are efficient, mounting and unmounting is easier, they use a simplified model, and new APIs are integrated. Traditional threads require expensive memory and OS scheduling; virtual threads are scalable, cheap, and simple. The following diagram compares platform threads vs virtual threads.
Platform threads are limited in numbers, expensive, and consume extra resources, and they are controlled by the OS. Virtual threads are unlimited, lightweight, scale massively, and they are controlled by the JVM.
Multithreading in Java interview questions ask you to compare them. The following table compares platform and virtual threads.
| Feature | Platform Threads (Traditional) | Virtual Threads (Modern) |
|---|---|---|
| Managed by | Operating System (OS) | JVM (via Project Loom) |
| Creation Cost | High (heavyweight) | Very low (lightweight) |
| Memory Usage | Large (MB per thread stack) | Small (KB per thread stack) |
| Scalability | Thousands of threads | Millions of threads |
| Scheduling | OS scheduler | JVM scheduler and mapped to carrier threads |
| Blocking Calls | Expensive (blocks OS thread) | Cheap (thread is parked, not blocked) |
| Context Switching | Costly | Efficient |
| Performance Type | Best for CPU-bound tasks | Best for I/O-bound tasks |
| Thread Pool Needed | Yes, to limit resource usage | Not required and are created per task |
| Debugging | Easier (familiar model) | Slightly complex in new model |
| Use Case | Parallel computation | High-concurrency applications |
Virtual threads are used in I/O-heavy tasks such as database calls, APIs, and file operations. They are used in web servers to manage millions of requests, and in microservices and network communication. They are used for simple blocking code for scalable performance.
Structured concurrency is a feature in Java 21 to handle multiple threads as a single unit of work. Rather than manually track threads, they are grouped in a scope to allow them to start, execute, and finish together.
Multithreading in Java interview questions on structured concurrency ask for code to improve readability, error handling, and cancellation. Code snippet for structured concurrency is:
Future<String> user = scope.fork(() -> fetchUser());
Future<String> order = scope.fork(() -> fetchOrder());
scope.join(); // Wait for all tasks
scope.throwIfFailed(); // Throw if any task failed
String result = user.resultNow() + " " + order.resultNow();
System.out.println(result);
Also Read: Top Java Interview Questions for Software Developers in 2026: What do Interviewers Test?
Java multithreading interview questions for freshers are on thread creation, lifecycle, basic synchronization, and avoiding deadlocks. Focus is on definitions and simple code snippets.
A thread is a lightweight unit of execution in a process. Multithreading is running multiple threads concurrently to improve performance and responsiveness.
A process is an independent program, while a thread is part of a process that shares memory.
In multitasking, an operating system runs multiple tasks and processes at the same time by sharing CPU time. It uses time-sharing and context switching, allowing tasks to run simultaneously.
The main thread is the first thread created at the start of a Java program that starts to execute the main() method. It is the starting point of any Java application created automatically by the JVM. It can create and start other threads. When the main thread finishes, the program can end.
To check if the thread is live, use the isAlive() method of the Thread class.
If the response is true, then the thread has been started and not yet finished.If the response is false, then the thread is not started OR already completed.
To check if the thread is live, use the isAlive() method of the Thread class.
If the response is true, then the thread has been started and not yet finished
If the response is false, then the thread is not started OR already completed
The blog presented several Java multithreading interview questions and answers with code snippets. Key topics for multithreading in Java interview questions and answers were discussed. Topics covered include core thread concepts and implementation, thread creation and execution, synchronization and locks, API, JVN, and several others.
The Java interview questions on multithreading and answers will help freshers and experienced developers to crack core Java multithreading interview questions. Read and practice coding in a Java IDE, run debugging and testing, understand how the code solves real-world problems, take tests, and mock interviews.
Multithreading permits applications to perform multiple tasks in less time, they are responsive, and scale with multi-core CPUs. Improved performance is seen in parallel execution in e-commerce apps, order processing, and payments.
Improved Responsiveness is achieved with non-blocking UI / APIs and ensures UO apps do not freeze or hang during API calls and file downloads.
Common Java multithreading interview questions for experienced candidates are on core thread concepts and implementation, thread creation and execution, and synchronization and locks.
Other Common Java multithreading interview questions for experienced developers are on concurrency problems, thread communication, Java Memory Model, concurrent collections, APIs, and others.
Beginners can prepare for Java multithreading interview questions with the following plan. The study can be spread over 2-3 weeks.
Time Zone:
Master ML interviews with DSA, ML System Design, Supervised/Unsupervised Learning, DL, and FAANG-level interview prep.
Get strategies to ace TPM interviews with training in program planning, execution, reporting, and behavioral frameworks.
Course covering SQL, ETL pipelines, data modeling, scalable systems, and FAANG interview prep to land top DE roles.
Course covering Embedded C, microcontrollers, system design, and debugging to crack FAANG-level Embedded SWE interviews.
Nail FAANG+ Engineering Management interviews with focused training for leadership, Scalable System Design, and coding.
End-to-end prep program to master FAANG-level SQL, statistics, ML, A/B testing, DL, and FAANG-level DS interviews.
Learn to build AI agents to automate your repetitive workflows
Upskill yourself with AI and Machine learning skills
Prepare for the toughest interviews with FAANG+ mentorship
Time Zone:
Join 25,000+ tech professionals who’ve accelerated their careers with cutting-edge AI skills
25,000+ Professionals Trained
₹23 LPA Average Hike 60% Average Hike
600+ MAANG+ Instructors
Webinar Slot Blocked
Register for our webinar
Learn about hiring processes, interview strategies. Find the best course for you.
ⓘ Used to send reminder for webinar
Time Zone: Asia/Kolkata
Time Zone: Asia/Kolkata
Hands-on AI/ML learning + interview prep to help you win
Explore your personalized path to AI/ML/Gen AI success
The 11 Neural “Power Patterns” For Solving Any FAANG Interview Problem 12.5X Faster Than 99.8% OF Applicants
The 2 “Magic Questions” That Reveal Whether You’re Good Enough To Receive A Lucrative Big Tech Offer
The “Instant Income Multiplier” That 2-3X’s Your Current Tech Salary
Join 25,000+ tech professionals who’ve accelerated their careers with cutting-edge AI skills
Join 25,000+ tech professionals who’ve accelerated their careers with cutting-edge AI skills
Webinar Slot Blocked
Time Zone: Asia/Kolkata
Hands-on AI/ML learning + interview prep to help you win
Time Zone: Asia/Kolkata
Hands-on AI/ML learning + interview prep to help you win
Explore your personalized path to AI/ML/Gen AI success
See you there!