Practice Questions — Multithreading Basics
← Back to NotesTopic-Specific Questions
Question 1
Easy
What is the output of the following code?
public class Main {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
}
}Every Java program starts with a main thread.
mainQuestion 2
Easy
What is the output?
Thread t = new Thread(() -> System.out.println("Hello"));
t.run();
System.out.println("World");run() is called directly, not start().
HelloWorldQuestion 3
Easy
What is the output?
Thread t = new Thread(() -> System.out.println("Background"));
System.out.println("State: " + t.getState());
t.start();
System.out.println("State: " + t.getState());Check the thread state before and after calling start().
State: NEWState: RUNNABLEBackground(Output order of last two lines may vary)
Question 4
Easy
What is the output?
Thread t = new Thread(() -> {
System.out.println(Thread.currentThread().getName());
});
t.setName("Worker");
t.start();setName() changes the thread's name before it starts.
WorkerQuestion 5
Easy
What is the output?
Thread t = new Thread(() -> {
for (int i = 0; i < 3; i++) {
System.out.println("A");
}
});
t.start();
t.join();
System.out.println("B");join() makes the main thread wait for t to finish.
AAABQuestion 6
Medium
What is the output?
Thread t = new Thread(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("Interrupted!");
}
System.out.println("Done");
});
t.start();
t.interrupt();interrupt() wakes a sleeping thread by throwing InterruptedException.
Interrupted!DoneQuestion 7
Medium
What is the output?
class MyThread extends Thread {
public void run() {
System.out.println("run");
}
public void start() {
System.out.println("start");
}
}
MyThread t = new MyThread();
t.start();The start() method is overridden. What does the overridden version do?
startQuestion 8
Medium
What is the output?
Thread t = new Thread(() -> System.out.println("Hello"));
t.start();
t.start(); // start() called twiceCan a thread be started more than once?
HelloThen
IllegalThreadStateException is thrown on the second start() call.Question 9
Medium
What is the output?
synchronized void methodA() {
System.out.println("A");
methodB();
}
synchronized void methodB() {
System.out.println("B");
}
// Called from one thread on the same objectCan a thread re-enter a synchronized method on the same object?
ABQuestion 10
Hard
What is the output?
Thread t1 = new Thread(() -> {
synchronized (Main.class) {
System.out.println("T1 locked");
try { Thread.sleep(100); } catch (InterruptedException e) {}
System.out.println("T1 done");
}
});
Thread t2 = new Thread(() -> {
synchronized (Main.class) {
System.out.println("T2 locked");
}
});
t1.start();
Thread.sleep(20); // give t1 time to acquire lock
t2.start();
t1.join();
t2.join();Both threads synchronize on the same lock (Main.class). T1 acquires it first.
T1 lockedT1 doneT2 lockedQuestion 11
Medium
What is the difference between
sleep() and wait()?Think about lock behavior and where each can be called.
sleep() pauses the current thread for a specified time and does NOT release any lock it holds. It can be called anywhere. wait() releases the lock on the object and waits until notify() or notifyAll() is called. It must be called inside a synchronized block. sleep() is a static method of Thread, while wait() is an instance method of Object.Question 12
Hard
Why should you prefer implementing Runnable over extending Thread?
Think about Java's inheritance model and the Executor framework.
Three reasons: (1) Java supports only single inheritance. If you extend Thread, you cannot extend any other class. Implementing Runnable preserves your inheritance slot. (2) Runnable separates the task (what to do) from the thread mechanism (how to run it), following better OOP design. (3) Runnable objects can be submitted to ExecutorService thread pools, enabling thread reuse and better resource management.
Question 13
Hard
What is the difference between
synchronized and volatile?Think about atomicity vs visibility.
synchronized provides both mutual exclusion (only one thread executes the critical section) and visibility (changes are flushed to main memory). volatile provides only visibility (reads/writes go to main memory) but NOT mutual exclusion. Use synchronized for compound operations (check-then-act, read-modify-write). Use volatile for simple flags that are written by one thread and read by others.Question 14
Hard
What are four conditions required for a deadlock to occur (Coffman conditions)?
All four must be present simultaneously for deadlock.
(1) Mutual Exclusion: At least one resource is held in a non-sharable mode. (2) Hold and Wait: A thread holds at least one resource and waits for another. (3) No Preemption: Resources cannot be forcibly taken from a thread; it must release them voluntarily. (4) Circular Wait: A circular chain of threads exists, each waiting for a resource held by the next. Eliminating any one condition prevents deadlock.
Mixed & Application Questions
Question 1
Easy
What is the output?
Thread t = new Thread(() -> System.out.println("Go"));
System.out.println(t.isAlive());
t.start();
System.out.println(t.isAlive());isAlive() returns true if the thread has been started and has not yet terminated.
falsetrueGo(The last two lines may appear in either order)
Question 2
Easy
What is the output?
Thread t = new Thread(() -> {
System.out.println("Running");
});
t.setDaemon(true);
t.start();
System.out.println("Main done");Daemon threads are terminated when all non-daemon threads finish.
Main done("Running" may or may not appear)
Question 3
Medium
What is the output?
Thread t1 = new Thread(() -> {
for (int i = 0; i < 3; i++) System.out.print("A");
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 3; i++) System.out.print("B");
});
t1.start();
t1.join();
t2.start();
t2.join();
System.out.println();join() after t1.start() means t1 finishes before t2 starts.
AAABBBQuestion 4
Medium
What is the output?
class Shared {
synchronized void method1() {
System.out.println("method1 start");
try { Thread.sleep(1000); } catch (InterruptedException e) {}
System.out.println("method1 end");
}
void method2() {
System.out.println("method2");
}
}
Shared obj = new Shared();
new Thread(() -> obj.method1()).start();
Thread.sleep(100);
new Thread(() -> obj.method2()).start();method2() is not synchronized.
method1 startmethod2method1 endQuestion 5
Medium
What is the output?
ExecutorService exec = Executors.newSingleThreadExecutor();
exec.submit(() -> System.out.println("Task 1"));
exec.submit(() -> System.out.println("Task 2"));
exec.submit(() -> System.out.println("Task 3"));
exec.shutdown();A single-thread executor runs tasks sequentially.
Task 1Task 2Task 3Question 6
Medium
What is the output?
Thread t = new Thread(() -> {
System.out.println("Priority: " + Thread.currentThread().getPriority());
});
t.setPriority(8);
t.start();
t.join();setPriority() sets the thread's priority before it starts.
Priority: 8Question 7
Hard
What is the output?
class Counter {
private int count = 0;
public synchronized void add(int n) {
for (int i = 0; i < n; i++) count++;
}
public int getCount() { return count; }
}
Counter c = new Counter();
Thread t1 = new Thread(() -> c.add(5000));
Thread t2 = new Thread(() -> c.add(5000));
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(c.getCount());add() is synchronized. Is there a race condition?
10000Question 8
Hard
What is the output?
Object lock = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("T1 waiting");
lock.wait();
System.out.println("T1 resumed");
} catch (InterruptedException e) {}
}
});
Thread t2 = new Thread(() -> {
try { Thread.sleep(500); } catch (InterruptedException e) {}
synchronized (lock) {
System.out.println("T2 notifying");
lock.notify();
}
});
t1.start();
t2.start();
t1.join();
t2.join();T1 calls wait(), T2 calls notify() after 500ms.
T1 waitingT2 notifyingT1 resumedQuestion 9
Easy
What is the difference between a thread and a process?
Think about memory sharing and weight.
A process is an independent program with its own memory space. A thread is a lightweight unit of execution within a process. Threads within the same process share heap memory but have separate stacks. Creating a thread is cheaper than creating a process. Threads communicate through shared memory; processes need IPC mechanisms.
Question 10
Hard
What is a thread pool, and why is it better than creating threads manually?
Think about the cost of creating and destroying threads.
A thread pool maintains a fixed number of reusable threads. Tasks are submitted to a queue, and available threads pick them up. Benefits: (1) Avoids the overhead of creating/destroying threads for each task. (2) Controls the maximum number of concurrent threads, preventing resource exhaustion. (3) Provides task queuing when all threads are busy. (4) Simplifies thread management code. Java's
ExecutorService (e.g., Executors.newFixedThreadPool()) provides this.Multiple Choice Questions
MCQ 1
Which method must you call to start a new thread in Java?
Answer: B
B is correct.
B is correct.
start() creates a new thread and calls run() on it. Calling run() directly executes the method on the current thread.MCQ 2
What interface should you implement to define a thread's task?
Answer: A
A is correct.
A is correct.
Runnable is the standard interface for defining a thread's task. Callable is also valid (it can return a value), but Runnable is the basic one.MCQ 3
What is the default name of the thread that runs the main() method?
Answer: B
B is correct. The main thread is named "main". Threads created with
B is correct. The main thread is named "main". Threads created with
new Thread() are named "Thread-0", "Thread-1", etc. by default.MCQ 4
What does Thread.sleep(1000) do?
Answer: B
B is correct.
B is correct.
Thread.sleep(1000) pauses the currently executing thread for 1000 milliseconds (1 second). It does not release any locks held.MCQ 5
Can a terminated thread be restarted?
Answer: C
C is correct. A terminated thread cannot be restarted. Calling
C is correct. A terminated thread cannot be restarted. Calling
start() on a terminated thread throws IllegalThreadStateException. You must create a new Thread object.MCQ 6
What does the synchronized keyword do?
Answer: B
B is correct.
B is correct.
synchronized acquires a lock on the object, ensuring mutual exclusion. Only one thread can execute synchronized methods/blocks on the same object at a time.MCQ 7
What exception does Thread.sleep() throw?
Answer: B
B is correct.
B is correct.
Thread.sleep() throws InterruptedException if another thread interrupts the sleeping thread. It is a checked exception that must be handled.MCQ 8
What is a daemon thread?
Answer: B
B is correct. Daemon threads are background threads (e.g., garbage collector). The JVM exits when only daemon threads remain. Set with
B is correct. Daemon threads are background threads (e.g., garbage collector). The JVM exits when only daemon threads remain. Set with
thread.setDaemon(true) before start().MCQ 9
What is the purpose of the join() method?
Answer: B
B is correct.
B is correct.
t.join() makes the calling thread (usually main) block until thread t terminates. It is used when you need the result of a thread before proceeding.MCQ 10
What does volatile ensure?
Answer: C
C is correct.
C is correct.
volatile ensures that reads and writes go to main memory, not a thread-local cache. It guarantees visibility but NOT atomicity or mutual exclusion.MCQ 11
What happens if you call wait() outside a synchronized block?
Answer: C
C is correct.
C is correct.
wait() must be called while holding the object's monitor (inside a synchronized block). Calling it without the lock throws IllegalMonitorStateException at runtime (not a compile error).MCQ 12
What is the difference between notify() and notifyAll()?
Answer: B
B is correct.
B is correct.
notify() wakes up one arbitrary thread waiting on the object's monitor. notifyAll() wakes up all waiting threads, and they compete for the lock. Use notifyAll() when multiple threads might be waiting for different conditions.MCQ 13
Which is NOT a way to prevent deadlock?
Answer: C
C is correct. Increasing thread priority does not prevent deadlock. Deadlock is about the order and duration of lock acquisition, not thread priority. Options A, B, and D are all valid deadlock prevention strategies.
C is correct. Increasing thread priority does not prevent deadlock. Deadlock is about the order and duration of lock acquisition, not thread priority. Options A, B, and D are all valid deadlock prevention strategies.
MCQ 14
What does Executors.newFixedThreadPool(5) create?
Answer: B
B is correct. A fixed thread pool maintains 5 threads that pick up tasks from a shared queue. If more than 5 tasks are submitted, excess tasks wait in the queue until a thread becomes available. Threads are reused, not destroyed after each task.
B is correct. A fixed thread pool maintains 5 threads that pick up tasks from a shared queue. If more than 5 tasks are submitted, excess tasks wait in the queue until a thread becomes available. Threads are reused, not destroyed after each task.
MCQ 15
What is the range of thread priorities in Java?
Answer: B
B is correct. Thread priorities range from 1 (MIN_PRIORITY) to 10 (MAX_PRIORITY), with 5 (NORM_PRIORITY) as the default. Priority is a hint to the scheduler and does not guarantee execution order.
B is correct. Thread priorities range from 1 (MIN_PRIORITY) to 10 (MAX_PRIORITY), with 5 (NORM_PRIORITY) as the default. Priority is a hint to the scheduler and does not guarantee execution order.
MCQ 16
Why should you use while (not if) before wait()?
Answer: B
B is correct. Spurious wakeups can cause a thread to wake up even without
B is correct. Spurious wakeups can cause a thread to wake up even without
notify(). Using while re-checks the condition after every wakeup, ensuring the thread only proceeds when the condition is truly met.Coding Challenges
Challenge 1: Thread-Safe Counter
EasyCreate a thread-safe Counter class with increment(), decrement(), and getCount() methods. Test it with two threads incrementing 10000 times each and verify the final count is 20000.
Sample Input
(No input required)
Sample Output
Expected: 20000
Actual: 20000
Use synchronized methods. Use join() to wait for threads before checking the count.
class Counter {
private int count = 0;
public synchronized void increment() { count++; }
public synchronized void decrement() { count--; }
public synchronized int getCount() { return count; }
}
public class ThreadSafeCounter {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) counter.increment();
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) counter.increment();
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Expected: 20000");
System.out.println("Actual: " + counter.getCount());
}
}Challenge 2: Parallel Array Sum
MediumSplit an array into two halves and compute the sum of each half in a separate thread. Then combine the results on the main thread. Compare with single-threaded sum.
Sample Input
Array: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
Sample Output
Thread-1 sum: 15
Thread-2 sum: 40
Total: 55
Use Runnable. Store each thread's result in a shared array or use a wrapper class.
public class ParallelSum {
public static void main(String[] args) throws InterruptedException {
int[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] results = new int[2];
int mid = data.length / 2;
Thread t1 = new Thread(() -> {
int sum = 0;
for (int i = 0; i < mid; i++) sum += data[i];
results[0] = sum;
System.out.println("Thread-1 sum: " + sum);
});
Thread t2 = new Thread(() -> {
int sum = 0;
for (int i = mid; i < data.length; i++) sum += data[i];
results[1] = sum;
System.out.println("Thread-2 sum: " + sum);
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Total: " + (results[0] + results[1]));
}
}Challenge 3: Producer-Consumer with Buffer
MediumImplement a bounded buffer (capacity 5) with produce() and consume() methods using wait/notify. The producer should produce 10 items, and the consumer should consume all 10.
Sample Input
(No input required)
Sample Output
Produced: 1
Produced: 2
...
Consumed: 1
Consumed: 2
...
Use synchronized, wait(), and notify(). Use a LinkedList as the buffer.
import java.util.LinkedList;
class BoundedBuffer {
private LinkedList<Integer> buffer = new LinkedList<>();
private int capacity;
public BoundedBuffer(int capacity) { this.capacity = capacity; }
public synchronized void produce(int item) throws InterruptedException {
while (buffer.size() == capacity) {
wait();
}
buffer.add(item);
System.out.println("Produced: " + item);
notifyAll();
}
public synchronized int consume() throws InterruptedException {
while (buffer.isEmpty()) {
wait();
}
int item = buffer.removeFirst();
System.out.println("Consumed: " + item);
notifyAll();
return item;
}
}
public class ProducerConsumerBuffer {
public static void main(String[] args) {
BoundedBuffer buffer = new BoundedBuffer(5);
Thread producer = new Thread(() -> {
try {
for (int i = 1; i <= 10; i++) {
buffer.produce(i);
Thread.sleep(100);
}
} catch (InterruptedException e) { e.printStackTrace(); }
});
Thread consumer = new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
buffer.consume();
Thread.sleep(200);
}
} catch (InterruptedException e) { e.printStackTrace(); }
});
producer.start();
consumer.start();
}
}Challenge 4: Thread Pool Task Processor
MediumUse ExecutorService with a fixed thread pool of 3 threads to process 8 tasks. Each task should print its ID and the thread executing it, then simulate work with Thread.sleep(). Print when all tasks are complete.
Sample Input
(No input required)
Sample Output
Task 1 on pool-1-thread-1
Task 2 on pool-1-thread-2
... (all 8 tasks)
All tasks completed.
Use Executors.newFixedThreadPool(). Use shutdown() and awaitTermination().
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class TaskProcessor {
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = Executors.newFixedThreadPool(3);
for (int i = 1; i <= 8; i++) {
int taskId = i;
pool.submit(() -> {
System.out.println("Task " + taskId + " on " + Thread.currentThread().getName());
try { Thread.sleep(500); } catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
pool.shutdown();
pool.awaitTermination(30, TimeUnit.SECONDS);
System.out.println("All tasks completed.");
}
}Challenge 5: Deadlock Detector and Fix
HardFirst, write code that creates a deadlock between two threads. Then fix it by using a consistent lock ordering strategy. Print messages to show the execution flow in both versions.
Sample Input
(No input required)
Sample Output
=== DEADLOCK VERSION ===
T1: locked A
T2: locked B
(hangs)
=== FIXED VERSION ===
T1: locked A
T1: locked B
T1: done
T2: locked A
T2: locked B
T2: done
Demonstrate the deadlock scenario. Fix by acquiring locks in the same order in both threads.
public class DeadlockFix {
private static final Object LOCK_A = new Object();
private static final Object LOCK_B = new Object();
public static void main(String[] args) throws InterruptedException {
System.out.println("=== FIXED VERSION (consistent lock order) ===");
// Both threads acquire LOCK_A first, then LOCK_B
Thread t1 = new Thread(() -> {
synchronized (LOCK_A) {
System.out.println("T1: locked A");
try { Thread.sleep(50); } catch (InterruptedException e) {}
synchronized (LOCK_B) {
System.out.println("T1: locked B");
}
}
System.out.println("T1: done");
});
Thread t2 = new Thread(() -> {
synchronized (LOCK_A) { // Same order: A then B
System.out.println("T2: locked A");
try { Thread.sleep(50); } catch (InterruptedException e) {}
synchronized (LOCK_B) {
System.out.println("T2: locked B");
}
}
System.out.println("T2: done");
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("No deadlock!");
}
}Need to Review the Concepts?
Go back to the detailed notes for this chapter.
Read Chapter NotesWant to learn Java with a live mentor?
Explore our Java Masterclass