Java Multithreading &
Synchronization
Master Guide — Solve Any Question
OOP + Threads + Synchronization + File Handling
1. Thread Lifecycle — States & Transitions
Thread States (know all 6!)
NEW → Thread object created, start() not called yet
RUNNABLE → Running OR ready to run (in thread scheduler queue)
BLOCKED → Waiting to acquire a synchronized lock
WAITING → wait() called — needs notify()/notifyAll() to resume
TIMED_WAITING→ sleep(ms), wait(ms), join(ms) — auto-wakes after time
TERMINATED → run() method finished or exception thrown
State Transition Flow:
Lifecycle Diagram (text form)
new Thread() ──► NEW
.start() ──► RUNNABLE ◄──────────────────────┐
│ │
synchronized block waiting ──► BLOCKED ──┘
│
wait() ──► WAITING ──► notify() ──► RUNNABLE
sleep(ms) ──► TIMED_WAITING ──► (timeout) ──► RUNNABLE
run() ends ──► TERMINATED
2. Creating Threads — 3 Ways
Way 1: Extend Thread
Extend Thread class
class BattingThread extends Thread {
private Batsman batsman;
BattingThread(Batsman b) { [Link] = b; }
@Override
public void run() {
// Thread logic here
[Link](4);
}
}
// Usage:
BattingThread t = new BattingThread(batsman);
[Link](); // ← ALWAYS start(), NEVER run()
Way 2: Implement Runnable (Preferred)
Implement Runnable interface
class BookingTask implements Runnable {
private String customerName;
BookingTask(String name) { [Link] = name; }
@Override
public void run() {
// Task logic here
[Link](customerName + " booking room...");
}
}
// Usage:
Thread t = new Thread(new BookingTask("Alice"));
[Link]();
// Or with lambda (Java 8+):
Thread t2 = new Thread(() -> [Link]("Lambda thread"));
[Link]();
Way 3: Implement Callable (returns a value)
Callable + Future
class FareCalculator implements Callable<Double> {
@Override
public Double call() throws Exception {
return 250.0; // Returns a value unlike Runnable
}
}
ExecutorService executor = [Link](3);
Future<Double> future = [Link](new FareCalculator());
Double result = [Link](); // Blocks until done
Key Rule
Always call .start() — NOT .run(). Calling .run() executes on the current thread — no new thread
is created!
start() → registers with scheduler → OS picks it up → calls run() internally.
3. Synchronization — The Core Concept
Synchronization ensures only ONE thread executes a critical section at a time, preventing race
conditions.
3.1 Synchronized Method
Synchronized method — locks 'this' object
class BankAccount {
private double balance = 5000;
// Only ONE thread can execute this at a time
public synchronized void withdraw(double amount) {
if (balance >= amount) {
balance -= amount;
[Link]("Withdrawn: " + amount);
} else {
[Link]("Insufficient balance!");
}
}
public synchronized void deposit(double amount) {
balance += amount;
}
public synchronized double getBalance() { return balance; }
}
3.2 Synchronized Block (Fine-Grained Control)
Synchronized block — better performance
public void processBooking(String customer) {
// Non-critical code runs freely (no lock needed)
[Link](customer + " processing...");
synchronized (this) { // ← Lock ONLY for critical section
// CRITICAL: check availability + assign room
if (availableRooms > 0) {
availableRooms--;
assignRoom(customer);
}
}
// More non-critical code...
}
3.3 Static Synchronization (Class-Level Lock)
Static synchronized — locks the Class object
class TicketCounter {
private static int ticketCount = 1000;
// Locks [Link] — NOT any instance
public static synchronized boolean bookTicket() {
if (ticketCount > 0) {
ticketCount--;
return true;
}
return false;
}
}
Type When to Use
synchronized method When entire method is critical — simple, clean
synchronized block When only part of method is critical — better perf
static synchronized When shared state is a static variable across all instances
ReentrantLock When you need tryLock(), timeout, or fairness control
4. wait() / notify() / notifyAll()
These methods enable inter-thread communication. Used to make one thread WAIT for a condition and
another thread SIGNAL when ready.
CRITICAL RULE
wait(), notify(), notifyAll() MUST be called inside a synchronized block/method.
Otherwise you get: IllegalMonitorStateException!
4.1 Producer-Consumer Pattern (The Universal Template)
Producer-Consumer — used in ALL 20 questions
class SharedResource {
private boolean resourceAvailable = false;
// CONSUMER — waits for resource
public synchronized void consume() throws InterruptedException {
while (!resourceAvailable) { // ← while loop, NOT if
wait(); // releases lock + sleeps
}
// Use the resource
resourceAvailable = false;
[Link]("Consumed!");
notifyAll(); // wake up waiting producers
}
// PRODUCER — signals when resource ready
public synchronized void produce() throws InterruptedException {
while (resourceAvailable) { // ← wait if already full
wait();
}
resourceAvailable = true;
[Link]("Produced!");
notifyAll(); // wake up waiting consumers
}
}
4.2 Hotel Room / Seat Booking Pattern
Wait when full, notify on release — used in Q2, Q4, Q5, Q6, Q13...
class BookingSystem {
private int availableRooms = 5;
public synchronized void bookRoom(String customer)
throws InterruptedException {
while (availableRooms == 0) { // ← WHILE not IF (spurious
wakeups!)
[Link](customer + " waiting for room...");
wait();
}
availableRooms--;
[Link](customer + " booked a room. Remaining: " +
availableRooms);
}
public synchronized void checkOut(String customer) {
availableRooms++;
[Link](customer + " checked out.");
notifyAll(); // ← wake ALL waiting customers
}
}
Why WHILE and not IF?
Spurious wakeups: A thread can wake up from wait() WITHOUT being notified (rare but possible
in JVM).
Use while loop so the condition is RE-CHECKED after waking up.
If you use if and a spurious wakeup occurs, the thread proceeds even though condition is still
false — BUG!
4.3 wait() vs sleep() — Critical Difference
Feature wait() vs sleep()
wait() Releases the lock. Thread waits until notify(). Must be in
synchronized block.
sleep(ms) Does NOT release the lock. Thread pauses for given time. Can be
called anywhere.
notified by wait() → needs notify() or notifyAll(). sleep() → wakes automatically.
used for wait() → inter-thread communication. sleep() → pausing execution.
5. OOP Patterns in All Questions
5.1 The Standard OOP Blueprint (Repeated Every Question)
Every question follows THIS structure — memorize it:
Blueprint: Interface → Abstract Class → Concrete Class
// Step 1: Interface (defines contract)
interface BattingOperations {
void playShot(int runs);
void getOut();
}
// Step 2: Abstract base class (shared fields + abstract method)
abstract class CricketPlayer {
protected String name;
protected int totalRuns;
protected boolean isOut;
CricketPlayer(String name) {
[Link] = name;
[Link] = 0;
[Link] = false;
}
abstract void performAction(); // force subclass to implement
}
// Step 3: Concrete class (extends abstract + implements interface)
class Batsman extends CricketPlayer implements BattingOperations {
Batsman(String name) { super(name); }
@Override
public synchronized void playShot(int runs) {
if (!isOut) {
totalRuns += runs;
[Link]("Runs: " + runs + " | Total: " +
totalRuns);
}
}
@Override
public synchronized void getOut() {
if (!isOut) { // ensures dismissal recorded only once
isOut = true;
[Link](name + " is OUT! Final: " + totalRuns);
}
}
@Override
void performAction() { [Link]("Batting!"); }
}
5.2 Enum Pattern
Enum with constructor (used in EVERY question)
enum ShotType {
SINGLE(1), DOUBLE(2), FOUR(4), SIX(6), OUT(0);
private final int runs;
ShotType(int runs) { // ← constructor
[Link] = runs;
}
public int getRuns() { // ← getter
return runs;
}
}
// Usage:
ShotType shot = [Link];
[Link]([Link]()); // prints: 6
[Link]([Link]()); // prints: SIX
// Iterating all values:
for (ShotType s : [Link]()) {
[Link](s + " = " + [Link]());
}
5.3 Abstract Method + Override Pattern
Abstract class with different implementations
abstract class Room {
protected int roomNo;
protected double basePrice;
abstract double calculateTariff(int days); // ← must override
}
class StandardRoom extends Room {
@Override
public double calculateTariff(int days) {
return basePrice * days; // base rate
}
}
class DeluxeRoom extends Room {
@Override
public double calculateTariff(int days) {
return basePrice * days * 1.5; // 1.5x multiplier
}
}
class SuiteRoom extends Room {
@Override
public double calculateTariff(int days) {
return basePrice * days * 2.5; // 2.5x multiplier
}
}
6. Custom Exceptions
Custom Exception Template
// Step 1: Create the exception class
class BedNotAvailableException extends Exception {
public BedNotAvailableException(String message) {
super(message);
}
}
// Step 2: Throw it
public void admitPatient(Patient p) throws BedNotAvailableException {
if (availableBeds == 0) {
throw new BedNotAvailableException(
"No beds available in " + wardType + " ward!");
}
availableBeds--;
}
// Step 3: Catch it
try {
[Link](patient);
} catch (BedNotAvailableException e) {
[Link]("Exception: " + [Link]());
}
Custom Exception Question Used In
BedNotAvailableExceptio Q6 — Hospital Bed Allocation
n
OutOfStockException Q8 — E-Commerce Inventory
BookNotAvailableExcepti Q10 — Library Book Borrowing
on
BorrowingLimitExceeded Q10 — Library Book Borrowing
Exception
InvalidTransactionExcept Bonus — Library Lending (Exam Paper)
ion
7. File Handling with Threads
7.1 Writing to a Log File (Thread-Safe)
Synchronized file writing — used in ALL file-handling questions
import [Link].*;
class Logger {
private static final String LOG_FILE = "[Link]";
// synchronized ensures no two threads write simultaneously
public static synchronized void log(String message) {
try (FileWriter fw = new FileWriter(LOG_FILE, true); // true =
append
BufferedWriter bw = new BufferedWriter(fw)) {
[Link](message);
[Link]();
} catch (IOException e) {
[Link]("Log error: " + [Link]());
}
}
}
// Usage from any thread:
[Link]("[DEPOSIT] Rs.5000 | Balance: Rs.15000 | " + new
[Link]());
7.2 Reading from a File
Reading file contents
public static String readFile(String filename) {
StringBuilder sb = new StringBuilder();
try (FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr)) {
String line;
while ((line = [Link]()) != null) {
[Link](line).append("\n");
}
} catch (IOException e) {
[Link]("Read error: " + [Link]());
}
return [Link]();
}
7.3 Unbuffered Streams (Exam Bonus Question)
FileWriter + FileReader WITHOUT BufferedWriter/Reader
// UNBUFFERED writing (FileWriter directly)
try (FileWriter fw = new FileWriter("[Link]", true)) {
[Link](message + "\n");
// No buffering — writes directly to disk
} catch (IOException e) { [Link](); }
// UNBUFFERED reading (FileReader directly)
try (FileReader fr = new FileReader("[Link]")) {
int ch;
StringBuilder sb = new StringBuilder();
while ((ch = [Link]()) != -1) { // reads char by char
[Link]((char) ch);
}
[Link]([Link]());
} catch (IOException e) { [Link](); }
8. Complete Thread Templates for Any Question
8.1 Multi-Thread with Shared Resource (Core Template)
This template solves Q2, Q4, Q5, Q6, Q9, Q13, Q15, Q18, Q19, Q20...
Universal Thread Template — adapt for any question
class SharedSystem {
private int capacity; // e.g., rooms, beds, seats, slots
private int current = 0;
SharedSystem(int capacity) {
[Link] = capacity;
}
// Called by customer/patient/student threads
public synchronized void requestAccess(String entityName)
throws InterruptedException {
while (current >= capacity) {
[Link](entityName + " waiting...");
wait();
}
current++;
[Link](entityName + " got access. Used: " + current +
"/" + capacity);
[Link](entityName + " accessed at " + new [Link]());
}
// Called when customer/patient/student leaves
public synchronized void releaseAccess(String entityName) {
current--;
[Link](entityName + " released. Available: " +
(capacity - current));
[Link](entityName + " released at " + new [Link]());
notifyAll();
}
}
// Thread for each customer/student/patient
class EntityThread extends Thread {
private SharedSystem system;
private String name;
private int stayDuration; // ms for simulation
EntityThread(SharedSystem sys, String name, int duration) {
[Link] = sys;
[Link] = name;
[Link] = duration;
}
@Override
public void run() {
try {
[Link](name);
[Link](stayDuration); // simulate stay
[Link](name);
} catch (InterruptedException e) {
[Link]().interrupt();
}
}
}
// Main — launch multiple threads
public class Main {
public static void main(String[] args) {
SharedSystem hotel = new SharedSystem(3); // 3 rooms
String[] customers = {"Alice", "Bob", "Charlie", "Dave", "Eve"};
for (String c : customers) {
new EntityThread(hotel, c, 2000).start();
}
}
}
8.2 Three-Thread Coordination (Q1 Cricket / Exam Pattern)
3 threads coordinating on shared object
// Thread 1: Active actor (batsman, student, customer)
class Thread1_Actor extends Thread {
private SharedObject obj;
@Override
public void run() {
// Does work, checks if stopped
while (![Link]()) {
[Link]();
try { [Link](500); } catch (InterruptedException e)
{ break; }
}
}
}
// Thread 2: Disruptor (fielder, librarian, checkout)
class Thread2_Disruptor extends Thread {
private SharedObject obj;
@Override
public void run() {
try { [Link](2000); } catch (InterruptedException e)
{ return; }
[Link](); // cause dismissal/return/checkout
}
}
// Thread 3: Monitor (umpire, moderator, supervisor)
class Thread3_Monitor extends Thread {
private SharedObject obj;
private Thread1_Actor actor;
private Thread2_Disruptor disruptor;
@Override
public void run() {
try {
[Link](); // wait for actor to finish
[Link](); // wait for disruptor to finish
} catch (InterruptedException e) { return; }
[Link]("[Monitor] All operations complete!");
[Link]("[Monitor] Final result: " + [Link]());
}
}
9. Common Mistakes & How to Fix Them
Mistake Fix
Calling .run() instead Always use .start() to create a new thread
of .start()
Using if instead of while Use while loop — spurious wakeups exist
for wait()
wait()/notify() outside Must be inside synchronized block/method
synchronized
Not handling Add try-catch or declare throws InterruptedException
InterruptedException
Race condition on Add synchronized to methods that read/write it
shared variable
Forgetting Check flag before acting, set it inside synchronized block
isOut/isAvailable check
Multiple dismissal Use if(!isOut) inside synchronized to ensure single update
records
File write corruption Make log() method static synchronized
across threads
Deadlock (threads wait Always lock in same order, avoid nested locks
for each other)
Not calling notifyAll() on notifyAll() in release method wakes all waiters
release
Deadlock Avoidance
Deadlock example and fix
// DEADLOCK — Thread A locks obj1 then obj2,
// Thread B locks obj2 then obj1 → they wait forever
// FIX: Always lock in the SAME ORDER
// Thread A: synchronized(obj1) { synchronized(obj2) { ... } }
// Thread B: synchronized(obj1) { synchronized(obj2) { ... } } ← same
order
// Or use tryLock() with timeout (ReentrantLock):
ReentrantLock lock1 = new ReentrantLock();
ReentrantLock lock2 = new ReentrantLock();
if ([Link](1, [Link])) {
try {
if ([Link](1, [Link])) {
try { /* critical section */ }
finally { [Link](); }
}
} finally { [Link](); }
}
10. Quick Reference Cheatsheet
Methods Cheatsheet
Method / Keyword Description
synchronized (method) Locks 'this'. Only 1 thread enters at a time.
synchronized (block) synchronized(obj) { } — locks specific object
wait() Releases lock, thread sleeps until notify(). Must be in synchronized.
notify() Wakes ONE randomly chosen waiting thread.
notifyAll() Wakes ALL waiting threads. Prefer over notify().
[Link](ms) Pauses thread for ms milliseconds. Does NOT release lock.
[Link]() Calling thread waits for thread t to finish.
[Link]() Creates new thread and starts execution of run().
[Link]() Sets interrupt flag. Use [Link]().interrupt() in catch.
volatile keyword Ensures variable is read/written from main memory, not cache.
AtomicInteger Thread-safe integer. incrementAndGet(), decrementAndGet(), etc.
Synchronized vs volatile vs Atomic
Tool Use When
synchronized Multiple operations must be atomic (check-then-act, read-modify-
write)
volatile Only ONE variable, only simple read/write, no compound operations
AtomicInteger/Long Single counter that's incremented/decremented by many threads
ReentrantLock Need tryLock, fairness, or multiple conditions (Condition objects)
The Universal Answer Template
Steps to solve ANY multithreading question
STEP 1: Identify shared resource (rooms, seats, balance, stock, beds...)
STEP 2: Create class with counter + synchronized requestAccess() +
releaseAccess()
STEP 3: Use while + wait() in requestAccess()
STEP 4: Use notifyAll() in releaseAccess()
STEP 5: Create Thread/Runnable for each actor (customer, student,
patient...)
STEP 6: In run(), call requestAccess → sleep → releaseAccess
STEP 7: Add synchronized Logger class for file output
STEP 8: In main(), create threads in a loop, start() all of them
STEP 9: Optionally join() threads to wait for all to finish before final
output
Master these 4 things and you can solve all 20 questions:
Interface → Abstract Class → Concrete Class | Enum with constructor | synchronized +
wait/notify | synchronized Logger