Java multithreading interview questions (with answers)
16 min readUpdated
Multithreading questions separate candidates who have only used Java from those who understand it. Here are the questions interviewers ask most, from start() versus run() to deadlocks, with programs whose output is the same on every run.
Threads
1. What is the difference between a process and a thread?
A process is a running program with its own memory. A thread is a path of execution inside a process; threads of one process share the heap, so they can work on the same objects, but each has its own stack.
2. How do you create a thread in Java?
Pass a Runnable (usually a lambda) to new Thread(...) and call start(), or, better in real code, submit tasks to an ExecutorService. Extending Thread also works but wastes your one chance to extend a class.
3. What is the difference between Runnable and Callable?
Runnable.run() returns nothing and cannot throw checked exceptions. Callable.call() returns a value and can throw; you submit it to an executor and get the result through a Future.
4. What is the difference between start() and run()?
start() creates a new thread that then calls run(). Calling run() directly is an ordinary method call on the current thread. Calling start() twice throws IllegalThreadStateException.
5. What are the states of a thread?
NEW, RUNNABLE, BLOCKED (waiting for a lock), WAITING and TIMED_WAITING (in wait, join or sleep), and TERMINATED. You can read the state with getState().
6. What does join() do?
t.join() makes the current thread wait until thread t has finished. It is the simplest way to wait for a result before using it.
7. What is a daemon thread?
A background thread that does not keep the JVM alive. When only daemon threads are left, the JVM exits. Garbage collection runs on daemon threads.
8. How do you stop a thread?
Ask it to stop: call interrupt() and let the thread check isInterrupted() or handle InterruptedException, or use a volatile boolean flag. Thread.stop() is deprecated because it can leave objects half-updated.
Race conditions and synchronization
9. What is a race condition?
A bug where the result depends on the timing of threads. The classic example is two threads running count++ on a shared counter: the read-add-write steps interleave and updates get lost.
10. Why is count++ not thread safe?
It is three steps: read count, add one, write it back. Two threads can both read 5 and both write 6, so one increment disappears. Fix it with synchronized, a lock or AtomicInteger.
import java.util.concurrent.atomic.AtomicInteger;
public class SafeCounter {
private int plain = 0;
private final AtomicInteger atomic = new AtomicInteger();
synchronized void incrementPlain() { plain++; }
public static void main(String[] args) throws InterruptedException {
SafeCounter c = new SafeCounter();
Runnable work = () -> {
for (int i = 0; i < 100_000; i++) {
c.incrementPlain();
c.atomic.incrementAndGet();
}
};
Thread t1 = new Thread(work);
Thread t2 = new Thread(work);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(c.plain); // always 200000: synchronized
System.out.println(c.atomic.get()); // always 200000: atomic
}
}Output
200000 200000
11. What does synchronized do?
Only one thread at a time can run a synchronized block or method on the same lock object (the instance for instance methods, the class for static methods). It also makes changes visible to the next thread that takes the lock.
12. What is the difference between a synchronized method and a synchronized block?
A synchronized method locks the whole method on this. A synchronized block locks only the lines that need it, on a lock object you choose, so other threads wait less.
13. What does volatile do, and when is it not enough?
volatile guarantees that every thread sees the latest value of a variable, which is right for a simple stop flag. It does not make compound actions such as count++ or check-then-act atomic; use locks or atomic classes for those.
14. What is the difference between synchronized and ReentrantLock?
Both give mutual exclusion. ReentrantLock adds tryLock() with a timeout, fair ordering, interruptible waiting and several Condition objects, but you must call unlock() in a finally block yourself.
15. What are atomic classes?
Classes such as AtomicInteger and AtomicLong that update a value safely without a lock, using a hardware compare-and-set. Ideal for counters and flags.
16. What is ThreadLocal?
A variable where each thread has its own copy, such as a per-request user or a SimpleDateFormat. Remove the value when done in thread pools, or it can leak into the next task.
Waiting and coordination
17. What is the difference between wait() and sleep()?
wait() is called on an object inside a synchronized block, releases the lock and waits for notify(). Thread.sleep() just pauses the current thread and keeps any locks it holds.
18. What is the difference between notify() and notifyAll()?
notify() wakes one waiting thread, notifyAll() wakes all of them. notifyAll() is safer when threads wait for different conditions; always wait in a while loop that rechecks the condition.
19. How do you implement producer-consumer?
Use a BlockingQueue such as ArrayBlockingQueue: producers call put(), which waits when the queue is full, and consumers call take(), which waits when it is empty. No manual wait/notify needed.
20. What are CountDownLatch, CyclicBarrier and Semaphore?
CountDownLatch lets threads wait until a count reaches zero (for example, all services started). CyclicBarrier makes a group of threads wait for each other and can be reused. Semaphore limits how many threads use a resource at once.
Executors and futures
21. Why use a thread pool instead of creating threads?
Creating a thread is expensive, and thousands of threads exhaust memory. A pool reuses a fixed number of threads for many tasks and gives you a queue, results through Future and a clean shutdown.
22. What is ExecutorService?
The standard thread-pool interface. You submit() tasks, get Future objects back and must call shutdown() when done, or the program may never exit.
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class PoolDemo {
public static void main(String[] args) throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(3);
List<Future<Integer>> results = new ArrayList<>();
for (int n = 1; n <= 5; n++) {
final int x = n;
results.add(pool.submit(() -> x * x)); // a Callable that returns a value
}
int total = 0;
for (Future<Integer> f : results) {
total += f.get(); // waits for that task
}
pool.shutdown();
System.out.println("Sum of squares: " + total);
}
}Output
Sum of squares: 55
23. What is the difference between submit() and execute()?
execute() takes a Runnable and returns nothing; an exception goes to the thread's handler. submit() returns a Future, and any exception is kept inside it and rethrown (wrapped) when you call get().
24. What is CompletableFuture?
A future you can chain without blocking: supplyAsync(...), then thenApply, thenCombine or exceptionally. It is used to run independent calls (for example, two web services) in parallel and combine the results.
25. How do you choose a thread pool size?
For CPU-heavy work, about the number of cores (Runtime.getRuntime().availableProcessors()). For work that mostly waits on I/O, more threads, or virtual threads in Java 21. Measure rather than guess.
26. What are virtual threads?
Lightweight threads from Java 21 that the JVM schedules onto a few real threads. You can run hundreds of thousands of them, which makes simple blocking code scale for I/O-heavy servers.
Concurrency problems
27. What is a deadlock, and how do you prevent it?
Two or more threads each hold a lock and wait for a lock the other holds, so none can continue. Prevent it by always taking locks in the same global order, by holding locks briefly, or by using tryLock() with a timeout.
28. What are livelock and starvation?
In a livelock threads keep reacting to each other and never make progress, although none is blocked. In starvation a thread never gets the CPU or lock it needs because others always win.
29. How do you find a deadlock in a running application?
Take a thread dump with jstack <pid> or jcmd <pid> Thread.print. The JVM reports "Found one Java-level deadlock" and shows which thread holds and waits for which lock.
30. What is an immutable object and why does it help with concurrency?
An object whose state never changes after construction, such as String or a record. Threads can share it freely with no locks, because there is nothing to race on.
Programs with threads often print in a different order on every run. The examples here wait for the threads (join, Future.get) before printing, so the output is always the same.
Frequently asked questions
Is multithreading asked in fresher interviews?
Usually the basics: what a thread is, start() versus run(), race conditions, synchronized and deadlock. Executors and CompletableFuture come up more for experienced roles.
What is the most important concurrency rule to remember?
Shared data that can change must be protected: with a lock, an atomic class or a concurrent collection. If you can, avoid sharing changeable data at all and use immutable objects.
Should I use synchronized or ReentrantLock?
Use synchronized by default; it is simpler and cannot be forgotten to unlock. Use ReentrantLock when you need tryLock, timeouts, fairness or several conditions.
Learn it step by step
Practise this, step by step
Short lessons with quizzes, practice and interview questions, from your first program to job-ready.