Multithreading Interview Questions and Answers for 2024

In Java, multithreading refers to a process that runs at least two threads simultaneously to ensure maximum CPU utilization. A Java thread is a lightweight process that requires fewer resources to create and share process resources. Java multitasking uses multithreading and multiprocessing, but we recommend using multithreading instead of multiprocessing. This is because the threads use a common memory area which helps to save memory and also exchanging content between threads is a bit faster than the process. You can also use this to get an idea of multithreading interview questions C++. Apart from C++, there are many languages that support multithreading, some examples include C++, Java, Python and Go programming languages. Multithreading saves time by allowing you to perform multiple operations simultaneously. The threads are independent, so it does not prevent the user from performing multiple operations at the same time, and if an exception occurs in one thread, it does not affect the other threads. These advantages make multithreading interview questions one of the most vital parts of coding interviews in all Information Technology industry.

  • 4.7 Rating
  • 78 Question(s)
  • 35 Mins of Read
  • 8853 Reader(s)


A thread is the smallest unit or task and is a lightweight sub-process executed independently. Each thread in the program runs on a different thread stack, and multiple threads form the complete program or applications. But they share common heap area.

The key benefits of Java multithreading include: 

  • Multiple threads can share single address spaces or memory. 
  • Threads share the same resources from the library but can execute differently. 
  • Different threads can be made to perform various functions to yield a different result. 

This is a frequently asked question in Multithreading interview questions.  

A process is part of the main program, while threads are subsets of processes. Processes have different address spaces in the memory, while threads share the same address space/heap memory.

Expect to come across this popular question in Java thread interview questions.  

Communication between synchronized threads is referred to as inter-thread communication. It is a core feature that helps to avoid thread-polling in Java. A particular thread can be paused through inter-thread communication to allow another thread to enter the block.

Some common methods that are used to perform inter-thread communication in Java are - notify(), wait() and notifyAll().

The wait() function, specified in the object class, is used to pause the current thread and wait until another thread calls the notify() function. Note that the wait() method needs to be called from a synchronized block to avoid an exception from occurring. After wait() call, current thread releases the lock and goes into wait state.

The different states in a thread life cycle include the following states: 

  1. New 
  2. Runnable/Running 
  3. Blocked/Waiting 
  4. Terminate 

Context switching is a feature through which the current state of a thread is saved for it to be restored and executed later. Through context switching, multiple processes can share the same CPU.

The join() method causes the current thread to stop running until the thread due to congregate with completes its task. It is a commonly used function that facilitates the execution of multiple threads in an organized manner.

A must-know for anyone heading into a Multithreading interview, this question is frequently asked in Multi threading interview questions.  

The basic use of sleep() method is to pause the current thread from executing and prioritize another thread that needs to be executed before the current thread gets executed.

A program is a set of instructions. It is stored on the computer disk and is therefore inactive. When the same program is loaded into main memory and the operating system allocates a bunch of memory (application) to that program, it is called a process. Therefore, a process is an executable program. So, we can say that it is active. 

When one process is loaded into main memory from secondary memory and another process is loaded from main memory into secondary memory, it is called a process switch.  
Process related information is stored as a PCB (Process Control Block). Process ID, process number, process status, program counter, registers and open files are usually stored on the PCB. This PCB is stored in a protected memory area to prevent normal user access as it contains important information. 

A process can spawn child processes using fork system calls. There are different types of schedulers that do different things for processes. 

Let us look at the types of schedulers and how they work:  

  • Short-term scheduler (CPU Scheduler): selects a process from main memory (ready queue) using a scheduling algorithm for processing in the CPU.  
  • Long Term Scheduler (Job Scheduler): This scheduler determines which processes should be placed in main memory (ready queue) and which should be placed in secondary memory.  
  • Medium Term Planning: Process switching is done by the medium term planner.  
    A single process can have multiple threads to execute different work. Each process has its own memory (called heap), which is not shared between different processes. 

A thread is a segment or part of a process that performs some of the tasks of the process. A process can have multiple threads that can run concurrently in the process. Each thread has its own thread stack, but multiple threads in a process share a common heap for that process. Therefore, threads are lightweight and faster than processes. 

Local variables are stored on the thread stack and are not shared between multiple threads and hence they are thread safe. Object fields are stored on the heap and are therefore shared across multiple threads.

An executable Java application is an example of a process. This application has some memory allocated at runtime. The application has a single thread called main which is responsible for executing our code.

Similarly, we can create multiple threads in the same application that perform different tasks.

It's no surprise that this one pops up often in multi threading interview questions.  

  1. A process is an executable program, while a thread is part of a process. 
  2. It takes more time to create and terminate a process than a thread. 
  3. A process consumes more resources (memory, IO, etc.) than threads. 
  4. Processes do not share memory (heap), while threads share common heap memory. 
  5. Changing the context of a process takes more time and is done by the operating system while changing the context of a thread takes less time and does not require an operating system call. 
  1. A process context switch occurs when the OS timer saves the current state (including PCB state) of the running program (current process) and switches to another process, while a thread context switch occurs when the CPU saves the current state of the thread. and switches the same process to another thread.  
  2. PCS occurs, the processor cache and Translational Lookaside Buffer are flushed, but the TCS processor cache and non-translational lookaside buffer retain their state. 
  3. PCS involves the high cost of changing the process control unit, making it less efficient and slower, while TCS does not require changing the circuit, so it is efficient and faster. 

With proactive scheduling, the highest-priority task is executed until it goes into a waiting or dead state, or a higher-priority task is created. With a time block, a task is completed at a certain time and then returns to  completed tasks. The scheduler then determines which task to run next based on priority and other factors.

When changing contexts, the state of the process (or thread) is preserved so that it can be restored and execution can be resumed later from the same point. Context switching allows multiple processes to share the same CPU.

A common question in Java Multithreading interview questions, don't miss this one.  

If you want to create a threaded instance, you have two options. 

First Option: 

We can use Thread class and write our own class extending the thread class to create thread objects. 

class ExampleThread extends Thread{   
public void run(){   
System.out.println("Thread has started and running");   
public static void main(String args[]){   
ExampleThread threadExample =new ExampleThread();   

Second Option: 

A thread also implements Runnable, so another way to start a thread is to create an anonymous subclass, override its run() method, and then call start(): 

class ExampleThread implements Runnable{   
public void run(){ 
System.out.println("thread is running...");   
public static void main(String args[]){   
ExampleThread exampleThread =new ExampleThread();   
Thread threadObj=new Thread(exampleThread);   

One of the most frequently posed Multithreading questions in Java, be ready for it.  

The state of a thread can be checked using the Thread.getState() method. The various states of a thread are described in the enumeration Thread. Status. They are: 

  • NEW — a new thread instance that has not yet been started with Thread.start(). 
  • RUNNING —  running thread. It is called running because it can run at any time or wait for the next time of the thread's timer. A NEW thread enters the RUNNING state when you call Thread.start() on it. 
  • BLOCKED — A running thread is blocked when it needs to access a synchronized section but cannot do so because another thread is holding the view of that section. 
  • WAITING — A thread enters this state when it is waiting for another thread to perform a specific action. For example, a thread enters this state when it calls the Object.wait() method on the screen it is on, or the Thread.join() method on another thread. 
  • TIMED_WAITING — This is the same as above, but a thread enters this state after calling timed versions of Thread.sleep(), Object.wait(), Thread.join(), and some other methods. 
  • TERMINATED —When the thread has completed the method and the thread enters the terminated state. 

This is a frequently asked question in Thread interview questions.  

The Runnable interface has a single run method. It represents a computation unit that must be run in a separate thread. The Runnable interface does not allow this method to return value or to throw unchecked exceptions.

The Callable interface has a single call method and represents a task that has a value. That’s why the call method returns a value. It can also throw exceptions. Callable is generally used in ExecutorService instances to start an asynchronous task and then call the returned Future instance to get its value.

Expect to come across this popular question in Java thread interview questions.  

A daemon thread is a thread that does not stop the JVM from exiting. When all non-daemon threads are finished, the JVM simply abandons any remaining daemon threads. Daemon threads are usually used to perform support or service operations for other threads, but you should note that they can be removed at any time. 

If you want to start a thread as a daemon,  use the setDaemon() method before the start() call. 

Thread d = new Thread(() -> System.out.println("Hello from daemon!")); 

Strangely, the message may not be printed if you run it as part of the main() method. This can happen if the main() thread terminates before the daemon has time to print the message. In general, you shouldn't do I/O on daemon threads because they can't even fill their last blocks or close resources if they're abandoned.

It's no surprise that this one pops up often in Multi threading interview questions.  

wait(): As the name suggests, this is a non-static method that causes the current thread to wait and sleep until some other thread calls the notify() or notifyAll() method on the monitor (locked) object. It simply releases the lock and is mostly used for interthread communication. It is defined in the object class and should only be called from a synchronized context. 


monitor.wait(); //Causes the current thread to wait until it is awakened, typically by being notified or interrupted.  

sleep(): As the name suggests, this is a static method that pauses or suspends the execution of the current thread for a certain period of time. It does not release the lock while waiting and is mostly used for pausing. It is defined in the thread class and does not need to be called from the synchronized context. 


Thread.sleep(1000); //Here Lock is held by the current thread even though it is going to sleep for 1 second 

A common question in Multithreading interview questions, don't miss this one.  

Class Lock: In Java, each class has a unique lock, often called a class-level lock. These locks are obtained using the "static sync" keyword and can be used to secure static data. This is typically used when you want to prevent multiple threads from accessing a synchronized block. 

public class ClassLevelLockExample   
  public void classLevelLockMethod()   
     synchronized (ClassLevelLockExample.class)   
            //add some cool stuff here       

Object locking: In Java, each object has a unique lock, often called an object-level lock. These locks are accessed with the "sync" keyword and can be used to protect non-static data. This is typically used when you want to synchronize a non-static method or block so that only a thread can execute the block of code on a specific instance of the class. 

public class ObjectLevelLockExample   
  public void objectLevelLockMethod()   
     synchronized (this)   
            //add some cool stuff here    

One of the most frequently posed Multi threading interview questions, be ready for it.  

User and daemon are basically two types of threads that are used in Java using the "thread class".

  • User thread (non-daemon thread): In Java, user threads have a fixed lifetime and are independent of other threads. The JVM (Java Virtual Machine) waits for some user thread to finish its task before terminating it. When the user threads terminate, the JVM terminates the entire program and its associated daemon threads.  
  • Daemon thread: In Java, daemon threads are basically called service provider that provides services and support to user threads. The Thread class basically has two methods for the daemon thread: setDaemon() and isDaemon().

This is a frequently asked question in Multithreading scenario based questions.  

In Java, we can create a daemon thread using setDaemon(true) thread class. It is used to mark the active thread as daemon or user thread. The isDaemon() method is usually used to check whether the current thread is a daemon or not. Returns true if the thread is a daemon, otherwise, returns false. 


A program that illustrates the use of the setDaemon() and isDaemon() methods. 

public class DaemonThreadExample extends Thread  
   public DaemonThreadExample(String name){  
   public void run()  
           System.out.println(getName() + " is a daemon thread");   
           System.out.println(getName() + " is a user thread");   
   public static void main(String[] args)  
       DaemonThreadExample t1 = new DaemonThreadExample ("thread1");  
       DaemonThreadExample t2 = new DaemonThreadExample (" thread2");  
       DaemonThreadExample t3 = new DaemonThreadExample (" thread3");   


thread1 is a daemon thread  
thread3 is a daemon thread  
thread2 is a user thread 

But  the setDaemon() method can only be called before the start() method, otherwise it will definitely throw an IllegalThreadStateException as shown below: 

public class DaemonThread extends Thread  
   public void run()  
       System.out.println("Thread name: " + Thread.currentThread().getName());  
       System.out.println("Check if its DaemonThread: "   
                       + Thread.currentThread().isDaemon());  
   public static void main(String[] args)  
       DaemonThread t1 = new DaemonThread();  
       DaemonThread t2 = new DaemonThread();  
       // Exception as the thread is already started on next line  


Thread name: Thread-0  
Check if its DaemonThread: false

A volatile variable is basically a keyword  used to provide visibility and handling of variable changes  in multi-threaded programming. This keyword cannot be used with classes and methods, but it can be used with variables. It is simply used to achieve thread safety for variables. When you mark a variable as volatile, all  threads can read its value directly from  main memory instead of the CPU cache, so  each thread gets the updated value of the variable.

Threads can communicate using three methods i.e., wait(), notify(), and notifyAll().

Expect to come across this popular question in Multithreading interview questions.  

Yes, it is possible. If both threads acquire locks on different objects, then they can execute concurrently without any problem.

The finalize() method is basically a method of the Object class that is specifically used to clean up unmanaged resources immediately before garbage collection. It is by no means intended to be called a normal method. After the finalize() method completes, the object is automatically destroyed.

It's no surprise that this one pops up often in Java multithreading interview questions.  

No, it's not possible at all. You need to call the start method to create a new thread otherwise run method won't create a new thread. Instead, it will execute in the current thread.

This question is a regular feature in Java thread interview questions, be ready to tackle it.  

Of course, it is possible. In multithreaded programming, each thread maintains its own separate stack area in memory because of which every thread is independent of each other rather than dependent.

  1. Communication between synchronized threads is called interthread communication. 
  2. Interthread communication is used in Java to avoid thread probing. A thread is suspended in a critical section and another thread is allowed to enter (or lock) the same critical section for execution. This can be obtained using the wait(), notify() and notifyAll() methods. 

The wait() method needs to be called from a synchronized method or non-static block as wait() is an object method and not a class method. Basically it is defined in object class as an object’s behavior.

A process where two or more actions / tasks of the same process run concurrently.


The termination flag or termination status is an internal thread flag that is set when a thread is terminated. Set this up by calling thread.interrupt() on the thread object.

If a thread is currently suspended within a method that throws an exception (wait, join, sleep, etc.), that method immediately throws a Canceled exception. A thread is free to handle this exception according to its own logic.

If there is no thread in such a method and thread.interrupt() is called, nothing special happens. The thread is responsible for periodically checking the interrupt state using the static Thread.interrupted() method or the isInterrupted() instance. The difference between these methods is that  static Thread.interrupted() clears the interrupt flag, while isInterrupted() does not.

This is a frequently asked question in Java thread interview questions.  

Executor and ExecutorService are two related interfaces of the java.util.concurrent framework. A launcher is a very simple interface with a single execution method that accepts executable instances for execution. In most cases, this is the interface that your task execution code should depend on.

ExecutorService extends the Executor interface with multiple concurrent task execution service handling and lifecycle control methods (task termination on termination) and more sophisticated asynchronous task processing methods, including Futures.

Expect to come across this popular question in Multithreading questions in Java.  

The ExecutorService interface has three standard implementations: 

  • ThreadPoolExecutor- This task execution uses thread selection. Once the thread has completed its task, it returns to the pool. If all threads in the group are busy, the task must wait its turn. 
  • ScheduledThreadPoolExecutor- It allows you to schedule a task instead of running it as soon as a thread is available. It can also schedule fixed rate or fixed delay tasks. 
  • ForkJoinPool is a special ExecutorService to handle recursive algorithm tasks. If you use a simple ThreadPoolExecutor for a recursive algorithm, you will quickly find that all your threads are busy waiting for lower levels of recursion to finish. ForkJoinPool implements the so-called job reservation algorithm, which allows more efficient use of existing threads. 

One of the most frequently posed Java thread interview questions, be ready for it.  

The Java memory model is part of the Java language definition described in Chapter 17. It determines how several threads use shared memory in a concurrent Java application and how data changes made by one thread are visible to other threads. Although JMM is quite short and concise, it can be difficult to understand without a strong mathematical background.

The need for a memory model is because the way your Java code accesses data doesn't happen at lower levels. The Java compiler, the JIT compiler, and even the CPU can rearrange or optimize memory writes and reads as long as the observable result of those reads and writes is the same.

A volatile field has special properties according to the Java memory model. Reading and writing a mutable variable are synchronous operations, which means that they have perfect ordering (all threads follow the same order of those operations).

Reading a constant variable will definitely track the last write to that variable in that order. If you have a field that is accessed by multiple threads and written to at least one thread, you should consider making it mutable, otherwise there is little guarantee that a particular thread will read from that field.

Another guarantee of volatility is the atomicity of writing and reading 6 -bit values (long and double). Without a volatile modifier, reading such a field may detect a value partially written by another thread.

  1. writing to a non-volatile int; 
  2. writing to a volatile int; 
  3. writing to a non-volatile long; 
  4. writing to a volatile long; 
  5. incrementing a volatile long? 

Writing to an int (32-bit) variable is guaranteed to be atomic, mutable or not. A long (6 -bit) variable can be written in two separate steps, for example on 32-bit architectures, so there is no guarantee of atomicity by default. However, if you specify a volatile modifier, access to the long variable is guaranteed on an atomic basis.

The increment operation is usually done in several steps (getting the value, changing it, and writing it back), so it is never guaranteed to be atomic, regardless of whether the variable is mutable or not. If you want to implement atomic addition of a value, you should use classes AtomicInteger, AtomicLong, etc.

A staple in Multithreading questions, be prepared to answer this one.  

The synchronized keyword before a block means that any thread entering this block has to acquire the monitor (the object in brackets). If the monitor is already acquired by another thread, the former thread will enter the BLOCKED state and wait until the monitor is released. 

synchronized(object) {    
    // ... 

A synchronized instance method has the same semantics, but the instance itself acts as a monitor. 

synchronized void instanceMethod() { 
   // ... 

For a static synchronized method, the monitor is the Class object representing the declaring class. 

static synchronized void staticMethod() { 
   // ... 

If a method is an instance method, the instance acts as a method monitor. Two threads that call the method on different occasions get different screens so that neither of them blocks.

If the method is static, the monitor is an object of the class. Both threads have the same monitor, so one of them will probably block and wait for the other to exit the synchronized method.

A thread that owns an object's monitor (for example, a thread that has entered a synchronized section protected by the object) can call object.wait() to temporarily release the monitor and allow other threads to acquire the monitor.

This can be done, for example, to wait for a certain condition. If another thread that acquired the monitor meets the condition, it can call object.notify() or object.notifyAll() and release the monitor. The notify method wakes up one thread in the waiting state, and the notifyAll method wakes up all the threads waiting for that monitor, all of which compete to regain the lock.

It's no surprise that this one pops up often in Multi threading interview questions.  

A deadlock is a state in a thread pool that cannot progress because each thread in the pool must acquire a resource that another thread in the pool has already acquired. The simplest case is when two threads each need to lock two resources to progress, the first resource is already locked by one thread, and the second is already locked by the other thread. These threads never lock either resource and, therefore, never progress.

Livelock is a case where multiple threads respond to conditions or events of their own making. An event occurs in one thread and must be processed in another thread. During this processing, a new event occurs, which must be processed in the first thread, and so on. Such threads live and do not block but still do not progress because they burden each other with unnecessary work.

Starvation is when a thread cannot obtain a resource because another thread (or threads) is using it for too long or has a higher priority. The thread cannot progress and thus cannot do any useful work.

A common question in Java thread interview questions, don't miss this one.  

The Fork/Join framework allows recursive algorithms to be parallelized. The main problem with a recursively parallel ThreadPoolExecutor is that it can quickly run out of threads because each recursive step requires its own thread while the threads on the stack are idle and waiting.

The starting point of the Fork/join framework is the ForkJoinPool class, which is an implementation of ExecutorService. It implements a job reservation algorithm where idle threads try to "steal" work from busy threads. This allows computation to be spread across different threads and progress using fewer threads than a normal thread pool would require.

We know that every object has a monitor that allows a thread to hold a lock on the object. But thread class does not contain monitors. A thread usually waits for control of an object (a lock) by calling the wait method of the object, and notifies other threads waiting on the same lock using the notify() or notifyAll() method. Therefore, these three methods are only called on objects and allow all threads to communicate with each thread created for that object.

One of the most frequently posed Java Multithreading interview questions, be ready for it.  

The Problems With Synchronized Keyword: 

  • For the shared resources, a thread may only obtain a lock on an item once. 
  • Other threads cannot lock after a thread has completed a synchronized block. 
  • Let's take a scenario and understand the starvation problem with multiple threads using the synchronized keyword. Suppose there are 10 threads (t1 to t10) and threads (t1 to t9) that acquire a lock and release the lock after executing a synchronized block. 
  • But there may be a situation where the t10 thread does not have a chance to execute a synchronized block when other threads are competing for the lock. So it can cause starvation and t10 has to wait a very long time to start the sync block. 

The Solution: Reentrant Lock: 

Java provides a better way to deal with the above problems that handle synchronization. 

  • The ReentrantLock class implements the Lock interface. This class is provided in java. util.concurrent package. import java.util.concurrent.locks.ReentrantLock; 
  • We must create an object of the ReentrantLock class and wrap the shared resource with the lock and unlock methods of this object. 
  • A thread wishing to execute a shared resource must lock it, and therefore all other threads trying to lock the object must wait for the current thread to release it. 
  • The beauty of ReentrantLock is that it allows the current thread to take multiple locks on the same lock object. Note that the wait count value is set to one the first time the current thread calls the lock() method. The wait counter is incremented by one each time this thread calls lock(). 
  • For each unlock request, the pending count is decremented by one, and if the pending count is 0, the resource is unlocked. 

Now let us see how we can use this mechanism: 

public void methodName(){ 
   try { 
   } catch(Exception e) { 
   } finally { 

In the above code snippet, you can notice that the thread must be locked before the shared resource can be executed. And after executing the try block, the thread finally releases the block lock using the unlock() method. Note that regardless of whether an exception occurs or not, the finally block is always executed. 

When we use a Java application, the operating system allocates some memory to that application to store and run the program. This application is called a process when it is running. Now there can be multiple threads in the same application and each thread can perform different tasks like IO, DB operations etc. 

All threads of the same process (application) share the mass memory of that process. On the other hand, each thread in this application has its own memory called thread stack and stores local variables in it. 

Note that when we create an object of a class, that object is stored on the heap, not the thread stack. The thread stack is private memory reserved for each thread, which only stores local variable

This question is a regular feature in Java thread interview questions, be ready to tackle it.  

Thread stack and heap: 

First, the Java Virtual Machine divides its memory into stack and heap memories. 

It stores all objects on the heap and stack local primitives and local object references. 

Most importantly, each thread, including the main thread, has its own private stack. Local primitives and local reference variables are stored on this stack (essentially the thread stack). 

Therefore one thread does not share its local variables with any other thread because these local variables and references are on the thread's private stack. Therefore, local variables are always thread-safe. 

Let us give a simple example to understand this: 

Thread-Safety for local variables: 

public class ThreadExample implements Runnable{ 
 public Integer counter; 
   public void run() { 
       counter = new Random().nextInt(); 
       Integer count = new Random().nextInt(); 
       System.out.println("counter : "+counter+" count : "+count); 
   public static void main(String[] args) { 
       ThreadExample threadExample = new ThreadExample(); 
       Thread thread1 = new Thread(threadExample); 
       Thread thread2 = new Thread(threadExample); 

The output of the above program: 

counter : -2066897874 count : 1775146445 
counter : 170327989 count : -1649657772 

In the example above, a ThreadExample instance is created on line 11, and two Thread objects are created on the next two lines (12 and 13), starting on lines 1 and 15. These threads execute the execute method of the same instance “threadExample”. 

When these two threads execute the execute method, they create their own copy of the local count variable on their private stack. Because those stacks are private, no two threads can share the same copy of the count variable. That's why it is thread-safe. Also, the counter variable is assigned a random integer, so it probably won't assign the same integer to the count variable. 

But both threads refer to the same instance ‘threadExample’ of ThreadExample class. This instance is on the heap and hence its field the counter is also on the heap. So there is only one copy of the field counter on the heap which is shared between these two threads. And hence there is a chance of collision between the threads which can lead to a race condition. That’s why the counter field is not thread-safe. 

  • start(): Simply put, the start() method is used to start or initiate a newly created thread. When the start() method is called, a new thread is created and this newly created thread executes the action stored in the run() method. The start() method can only be called once. 
  • run(): Simply put, the 'run()' method will act as any normal method and will not start or run thread. When the run()un  method is called, a new thread is not created, as is the case with the start() method. This method is called by the current thread. The run() method can be called multiple times. 

This is a frequently asked question in Multi threading interview questions.  

A thread pool is simply a set of worker threads, pre-initialized at startup, that can be used to perform tasks and return to the pool when they are finished. This is called pool threads, where a pool of threads of fixed size is created.  

By reducing the number of threads in an application and managing their life cycle, the performance problem can be mitigated by thread pooling. Using threads can improve performance and improve system stability. The java.util.concurrent.Executors class typically provides factory methods for creating thread pools.

The join() method is usually used to pause the execution of the current thread unless the specified thread on which the join is called is dead or completed. This method can be used to block a thread until another thread finishes. It connects the beginning of the execution of a thread to the end of the execution of another thread. This is considered the last method of the thread class.

Garbage collection is basically a process of managing memory automatically. It uses several GC algorithms among which the popular one includes Mark and Sweep. The process includes three phases i.e., marking, deletion, and compaction/copying. In simple words, a garbage collector finds objects that are no longer required by the program and then delete or remove these unused objects to free up the memory space.

Expect to come across this popular question in Multithreading questions.  

The Lock interface was introduced in Java 1.5 and is generally used as a synchronization mechanism to provide important locking functionality. 

Advantages of using the Lock interface compared to the sync block: 

  • The methods of the Lock interface, ie. Lock() and Unlock(), can be called by different methods. This is the main advantage of the lock interface over the synchronized block because the synchronized block is completely contained in a single method. 
  • The Lock interface is more flexible and, unlike the synchronization block, ensures that the longest waiting thread has a fair chance to start when the fair flag is set to true. 

A deadlock is a situation where each thread is waiting for resources held by other waiting threads. This situation causes the thread to fail to start, which causes the program to pause and break the code at runtime.

It's no surprise that this one pops up often in Java Thread interview questions.  

A Deadlock situation can be detected by running the executable code on cmd and subsequently collecting the thread dump. If a deadlock situation is present, the cmd will throw up a message.

This is one of the most common Java Multithreading interview questions asked in technical interviews. 

Deadlock situations in Java can be avoided by:

By way of avoiding nested thread locks and providing locks to only one thread at a time can help in reducing the risk of deadlock occurrence.

By using thread join – this function helps the thread to wait for other threads to execute before its own execution, thereby preventing multiple threads from waiting for resources used by other threads.

Individual threads do have their stacks in multithreaded programming. Each thread is independent of the other and maintains its own stack in the memory.

Thready safety can be achieved if multiple threads can use a particular class function without the occurrence of the race condition. In Multithreaded programming, thread safety can be achieved by: 

  • Using of atomic wrapper class 
  • Using of a volatile keyword 
  • Using a lock-based mechanism 
  • Using Synchronization mechanism

Atomic operations are performed in a single task unit without interfering with other operations. In multi-threaded environments, atomic operations are required to avoid data inconsistencies. int is not an atomic operation. So, when one thread reads this value and increments it by one, the other thread reads the original value, resulting in an incorrect result.  

To solve this problem, we need to make sure that the number of sum operations is atomic. We can do this using synchronization, but java.util.concurrent.atomic in Java 5 provides wrapper classes for int and long that can be used to achieve this. atomically without synchronization.

In synchronization, we don’t know how much time a thread will get a chance after a previous thread has released the lock. This can lead to the problem of starvation whereas, in the case of the lock, we have its implementing class reentrant lock which has one of its constructors which lets you pass fairness property as one of its arguments that let the longest waiting thread get the chance to acquire the lock.  

In synchronization, if a thread is waiting for another thread, then the waiting thread won’t do any other activity which doesn’t require lock access but with the lock interface, there is a trylock() method with which you can try to access the lock and if you don’t get the lock you can perform other alternate tasks. This helps to improve the performance of the application.  

There is no API to check how many threads are waiting for a particular lock whereas this is possible with lock interface implementation class ReentrantLock methods.  

Using the lock interface One can get better control of locks, with the methods such as holdCount() method which is not found with synchronization.

One of the most frequently posed Thread interview questions, be ready for it.  

Java 5 introduced the Executor framework with the java.util.concurrent. Executor interface. The Executor Framework is a framework for standardizing the invocation, scheduling, execution and management of asynchronous tasks according to execution conventions. Creating many threads without a maximum limit can cause the application to run out of memory and hence the application will crash.  

So creating a ThreadPool is a better solution because a limited number of threads can be pooled and reused once they are put again in the thread pool. The Executor framework makes it easy to create thread pools in java for multi-threading applications.

This is a frequently asked question in Java Multithreading interview questions for experienced.  

Actually, Java 5 introduced java.util.concurrent.Callable interface in concurrency package that is similar to Runnable interface but can return any Object and throw Exception. The Callable interface uses java generics to define the return type of the Object. Executors class provides useful methods to execute Callable in a thread pool.  

Since callable tasks run parallel, we have to wait for the returned Object. Callable tasks return java.util.concurrent.Future object. Using Future we can find out the status of the Callable task and get the returned Object. It provides the get() method that can wait for the Callable to finish and then return the result.

Thread or process context switching is basically an important feature of multithreading. It is referred to as switching of CPU from one thread or process to another one. It allows multiple processes to share the same CPU cores. In context switching, the state of thread or process is stored on PCB or TCB, so that the execution of the thread can be resumed later if required.

FutureTask is the base implementation class of the Future interface and we can use it with Executors for asynchronous processing. Most of the time we don't need to use FutureTask class but it comes really handy if we want to override some of the methods of the Future interface and want to keep most of the base implementation. We can just extend this class and override the methods according to our requirements.

Expect to come across this popular question in Java multithreading questions.  

BlockingQueue is typically used for one thread to create objects to be consumed by another thread. Here is a diagram that illustrates this principle:

The "producing thread" continues to create new objects and add them to the BlockingQueue until the queue reaches some upper limit. It's a limit, in other words. When the blocking queue reaches its upper limit, the creating thread is blocked from adding a new object. It remains blocked until the consuming thread lives the object.  

"Consumer thread" removes objects from the block queue to process them. If a consuming thread tries to remove an object from an empty queue, the consuming thread is blocked until the producing thread enqueues the object. 

CyclicBarrier and CountDownLatch, both required to handle multi-threaded programming. But there are differences between them as shown below: 

  • CyclicBarrier: It is a tool to synchronize the processing of threads using some algorithm. This allows a set of threads to wait for each other until they reach a common execution point or breakpoints and then resume execution. The same CyclicBarrier can be reused, even if the barrier is broken by placing it. 
  • CountDownLatch: This is a tool that allows main threads to wait for other threads to perform mandatory operations and finish them. Simply put, it ensures that a thread waits for another thread to finish before executing. The same CountDownLatch cannot be reused when the count reaches zero. 

It's no surprise that this one pops up often in Multithreading interview questions.  

The Executors class provides helper methods for the Executor, ExecutorService, ScheduledExecutorService, ThreadFactory, and Callable classes. The Executors class can be used to easily create a thread pool in Java and is also the only class that supports running Callable applications.

Some important concurrent API enhancements are: 

  1. ConcurrentHashMap compute(), forEach(), forEachEntry(), forEachKey(), forEachValue(), reduce(), merge()  and search() methods. 
  2. CompletableFuture that may be explicitly completed (setting its value and status). 
  3. Executors newWorkStealingPool() method to create a work-stealing thread pool using all available processors as its target parallelism level. 

Interthread communication, as the name suggests, is a process or mechanism that allows multiple threads to communicate with each other. It is specifically used in Java to prevent thread polling and can be obtained by the wait(), notify() and notifyAll() methods.

  • ConcurrentHashMap: The underlined data structure for ConcurrentHashMap is Hashtable. ConcurrentHashMap class is thread-safe i.e. multiple threads can operate on a single object without any complications. At a time any number of threads are applicable for a read operation without locking the ConcurrentHashMap object which is not there in HashMap. In ConcurrentHashMap, the Object is divided into a number of segments according to the concurrency level. 
  • Hashtable: This is a thread-safe inheritance class introduced in old Java versions to store key or value pairs using a hashtable. Unlike ConcurrentHashMap, it does not provide unlocked reads. It just locks the entire map on replay. ConcurrentHashMap and Hashtable are both thread-safe, but unlike Hashtable, ConcurrentHashMap generally avoids read locks and improves performance. 

ConcurrentHashMap also provides non-locking numbers, unlike Hashtable. Therefore, ConcurrentHashMap is considered faster than Hashtable, especially when the number of readers is more than the number of writers.

Expect to come across this popular question in Java Multithreading interview questions for experienced.  

A semaphore is considered a thread synchronization construct that is typically needed to control and manage access to a shared resource using counters. It just sets the thread limit. The semaphore class is defined in the java.util.concurrent package and can be used to send signals between threads to prevent missing signals or to protect critical parts. It can also be used to implement resources or a limited pool.

So nothing happens unless we override the run() method. The compiler does not show an error. This calls the run() method of the thread class and we just don't get the result because the run() method has an empty implementation.  

class ThreadExample extends Thread {  
  //don't override run() method  
public class DontOverrideRun {  
  public static void main(String[] args) {  
         System.out.println("Main thread has started");  
         ThreadExample thread1=new ThreadExample ();  
         System.out.println("Main thread has completed");  


Main thread has started 
Main thread has completed

Multithreading is a concept that appears in many programming languages. Although multithreaded languages can run on a single thread, multithreading cannot be disabled in languages that do not support this feature. C, JAVA, C# etc. are languages that support multithreading. JavaScript is a single-threaded language.


Top Multithreading Interview Questions Tips and Tricks

  1. While preparing for java developer positions, it becomes necessary to prepare java multithreading interview questions as it is one of the interesting and tricky topics where the interviewer checks the ability how you think to improve the performance of multithreaded applications and overall resource consumption. 

  2. Always answer the multi-threading questions by providing the right use cases for the asked questions. Be precise and concise with the concepts and answers. If you don’t know some concepts, then try to revise those using this IQA. 

  3. Once you learn and master the multithreading interview questions provided in this IQA, you should try to drive the interviews toward the topics that you know the best. 

  4. If possible, you should also aim to build some sample applications so that you can get a complete understanding and working of threads. 

  5. Once you know the basics, you start learning intermediate and advanced questions. One should note that just reading and learning the questions isn't sufficient, and hence you should also practice the programs given in this IAQ. Practice more and more multithreading coding questions. 

  6. You should start with basic multi-threading questions where you should get familiar with the whats and hows of multithreading. The multithreading questions in java is a good way to go about it. 

How to Prepare for Java Thread Interview Questions?

  1. Multithreading is a core concept for Java developers. When employers are interviewing for a Java developer position, they will likely want to know the extent of your multi-threaded knowledge. You can work on this by practicing Python multithreading interview questions. 

  2. You should start reading basic and entry-level multi-threaded interview questions and practice threading sample interview questions and examples. Once you are comfortable with the basics, choose intermediate topics and learn them thoroughly. As soon as you have gained enough knowledge about basic and intermediate topics, you can start with advanced topics, including Android multithreading interview questions.

  3. As part of your interview preparation, take the time to review common interview questions about multithreading to demonstrate your experience and skills to the hiring manager. Make sure you practice multithreading coding interview questions for this. Earn Java certification and stay updated on multithreading.  

Top Roles with Multithreading Skill

  • Software Engineer 
  • JAVA Developer / Full Stack Developer 
  • Senior Java Engineer 
  • Java Spring Boot Microservices Developer 
  • Lead Developer Java 
  • Senior Java Developer 
  • Full Stack Developer (React & Java) 

Top Companies looking for these Roles

  • TCS 
  • IBM 
  • Capgemini 
  • Infosys 
  • Accenture 
  • Cognizant 
  • Wipro 
  • Deloitte 
  • Oracle 
  • HCLTech 

What to Expect in Java Multithreading Interview Questions?

Interviewers start asking about how much comfortable you are on multithreading topics. We are dividing topics that are expected to know at 3 levels: 

1. Beginner Level Concepts  

Processes and Threads, Process and thread context block, heaps and stacks, how to create threads using Thread class and Runnable interfaces, critical section, race condition, starvation and deadlocks, class level object and object level locks(monitor), synchronized block and methods, static block and static method with synchronized keywords, primitive data variable thread safety, what is main thread and thread states. 

2. Intermediate Level Concepts  

Lock interface, when to use atomic keyword and volatile keywords, count down latch, cyclic barrier, cache coherence, producer and consumer problem, thread communication, what are wait(), notify() and notifyAll() methods, why these methods are in the object class, Thread states and how a thread transition to these states, reentrant lock. 

3. Advanced Concepts 

Multithreading interview questions for experienced often include the executor and ExecutorService frameworks and their mechanism, Executors working, thread scheduling algorithms, threading synchronization using semaphores, ScheduledExecutorService, thread pool, etc. 


Multithreading is essential for every programming language and for building modern software applications. Multithreading also leads to minimization and more efficient use of computing resources like optimized zed CPU usage, less memory footprint, and higher efficiency of over applications. Also, the application responsiveness is improved as requests from one thread do not block requests from other threads. 

Multithreading makes an application execute multiple tasks in parallel and thus use optimal resources for better throughput and enhanced efficiency. This article has discussed the most important java multithreading interview questions at all levels, and these frequently asked interview questions and answers can help you clear the multithreading interviews. Enroll in a Programming class and stay ahead of the crowd. This will help your CV land in interviews. 

Read More