Junior 5 min · March 05, 2026

Java Thread States — Lock During I/O Causes BLOCKED

A 30-second 503? Check thread dumps for BLOCKED threads on a lock held during I/O — exactly the real production incident we decode step by step.

N
Naren · Founder
Plain-English first. Then code. Then the interview question.
About
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • Java threads cycle through 6 states: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED
  • state() method reveals the current state in a thread dump
  • BLOCKED and WAITING are not the same — monitor contention vs indefinite park
  • TIMED_WAITING is WAITING with a timeout — always bound
  • Thread state transitions are driven by JVM internals and OS scheduling
  • Biggest mistake: treating RUNNABLE as "actively running" — it includes ready-to-run
Plain-English First

Imagine a chef in a restaurant kitchen. Sometimes they're actively cooking (RUNNING). Sometimes they're waiting for ingredients to arrive (WAITING). Sometimes a timer is going off and they'll be ready in 30 seconds (TIMED_WAITING). Sometimes another chef is using the stove and our chef is standing right there ready to grab it the moment it's free (BLOCKED). Before their shift starts, they haven't even put on their apron yet (NEW). When the shift ends and the kitchen closes, they're done for the night (TERMINATED). Java threads are exactly like that chef — and the JVM is the kitchen manager deciding who gets the stove.

Every production outage involving threads — the deadlock that froze your payment service at 2 AM, the thread pool that silently starved under load, the race condition that corrupted user data — traces back to a misunderstanding of what a thread is actually doing at any given moment. The Java thread lifecycle isn't just an academic diagram you memorize for interviews. It's the mental model that lets you read a thread dump, diagnose a hung application, and design concurrent systems that hold up under real traffic.

The problem is that most resources treat the lifecycle as a static state machine — here are the six boxes, here are the arrows, done. But threads don't live in boxes. They transition between states in ways that depend on OS scheduling, JVM implementation details, monitor ownership, and the specific flavor of waiting you've asked them to do. Miss those nuances and you'll write code that looks correct, passes unit tests, and then silently misbehaves in production with 200 concurrent users.

By the end of this article you'll be able to read a real thread dump and know exactly what each thread is doing and why. You'll understand the difference between BLOCKED and WAITING at the JVM level — not just the textbook definition. You'll know which state transitions are guaranteed, which are platform-dependent, and which ones hide the bugs that take senior engineers days to find. Let's build that mental model from the ground up.

The 6 Thread States — What Each Actually Means

Java defines six thread states in java.lang.Thread.State. They're not just labels — each maps to a specific JVM or OS condition.

  • NEW: Thread created but start() not called. Not yet alive.
  • RUNNABLE: Thread is executing in the JVM (or ready to execute, waiting for CPU). Includes both running and ready-to-run.
  • BLOCKED: Thread is waiting for a monitor lock to enter a synchronized block/method.
  • WAITING: Thread is waiting indefinitely for another thread to perform a specific action (e.g., wait(), join(), park()).
  • TIMED_WAITING: Same as WAITING but with a timeout (sleep, wait(timeout), join(timeout), parkNanos).
  • TERMINATED: Thread has completed (run() finished or exception).

The key insight: RUNNABLE does not mean 'using CPU right now'. It means the thread is eligible for scheduling. The OS decides when it actually runs. This is why busy-wait loops (while(!flag)) keep a thread in RUNNABLE but waste CPU.

io/thecodeforge/thread/ThreadStateDemo.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package io.thecodeforge.thread;

public class ThreadStateDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            // thread will be RUNNABLE during execution
            try {
                Thread.sleep(1000); // TIMED_WAITING
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }, "demo-thread");

        System.out.println("After creation: " + t.getState()); // NEW
        t.start();
        System.out.println("After start: " + t.getState()); // RUNNABLE
        Thread.sleep(200);
        System.out.println("Mid-sleep: " + t.getState()); // TIMED_WAITING
        t.join();
        System.out.println("After join: " + t.getState()); // TERMINATED
    }
}
Output
After creation: NEW
After start: RUNNABLE
Mid-sleep: TIMED_WAITING
After join: TERMINATED
Production Insight
In production, BLOCKED threads are the silent killers. A single thread stuck on I/O while holding a lock can cascade into a full system freeze. Monitor 'jstack' output for BLOCKED states — especially if multiple threads are blocked on the same monitor. That's a deadlock or lock contention waiting to happen.
Rule: If you see more than 5 threads BLOCKED in a dump, investigate immediately.
Key Takeaway
Six states, but only three matter in debugging: BLOCKED, WAITING, RUNNABLE.
BLOCKED = lock contention.
WAITING = missing signal.
RUNNABLE = maybe running, maybe just ready.
Punchline: Read the state, then find the root cause.

State Transitions — The Arrows Between the Boxes

  • NEW → RUNNABLE: Calling start().
  • RUNNABLE → BLOCKED: Attempting to enter a synchronized block/method without the lock. JVM puts you on the monitor's entry set.
  • BLOCKED → RUNNABLE: The lock holder releases the lock (exits synchronized block).
  • RUNNABLE → WAITING: Calling Object.wait(), Thread.join(), or LockSupport.park(). Thread is put in the wait set of the monitor.
  • WAITING → RUNNABLE: Another thread calls notify()/notifyAll() on the same monitor, or the thread is interrupted. But the thread must re-acquire the lock before proceeding — so it goes to BLOCKED first, then RUNNABLE.
  • RUNNABLE → TIMED_WAITING: Thread.sleep(time), wait(timeout), join(timeout), parkNanos().
  • TIMED_WAITING → RUNNABLE: Timeout expires, or notify/interrupt.
  • RUNNABLE → TERMINATED: run() completes.

The critical detail: after notify(), the waiting thread doesn't run immediately. It must re-acquire the monitor lock. This is why waiting code should always loop on the condition (spurious wakeup).

io/thecodeforge/thread/TransitionDemo.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package io.thecodeforge.thread;

public class TransitionDemo {
    private static final Object lock = new Object();
    private static boolean ready = false;

    public static void main(String[] args) throws InterruptedException {
        Thread waiter = new Thread(() -> {
            synchronized (lock) {
                while (!ready) { // must loop
                    try {
                        lock.wait(); // RUNNABLE -> WAITING
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
                System.out.println("Condition met!");
            }
        }, "waiter");
        waiter.start();
        Thread.sleep(500); // let waiter get to WAITING
        System.out.println("Waiter state: " + waiter.getState()); // WAITING

        Thread notifier = new Thread(() -> {
            synchronized (lock) {
                ready = true;
                lock.notify(); // transition: WAITING -> BLOCKED -> RUNNABLE
            }
        }, "notifier");
        notifier.start();
        waiter.join();
        System.out.println("Final state: " + waiter.getState()); // TERMINATED
    }
}
Output
Waiter state: WAITING
Condition met!
Final state: TERMINATED
The Bouncer Analogy
  • Entry set: Threads trying to enter the synchronized block (BLOCKED). Bouncer holds them back until the current occupant leaves.
  • Wait set: Threads that called wait() (WAITING). They voluntarily stepped aside and wait for a signal from the bouncer.
  • When notify() is called, one thread moves from wait set to entry set. It's still BLOCKED until it actually grabs the lock.
  • Multiple notify() calls move multiple threads — but only one gets the lock at a time.
  • Always wait inside a while loop — because of spurious wakeups and the gap between notify() and lock acquisition.
Production Insight
A classic production fail: A thread calls notify() but the condition the waiting thread checks is still false because of a race. The waiting thread wakes, checks the condition, finds it false, and goes back to WAITING. The notifier never calls notify() again, so the waiter waits forever. This is why the 'while loop' around wait() is non-negotiable.
Debugging tip: If you see threads in WAITING but no BLOCKED threads on the same monitor, the notifier may have fired too early or only once.
Key Takeaway
After notify(), the waiting thread must still re-acquire the lock (BLOCKED) before proceeding.
Always loop on the condition when using wait().
Spurious wakeups are real — the JVM spec says they can happen.
Punchline: If your wait() isn't in a while loop, you'll hit a production bug within a year.

BLOCKED vs WAITING — The JVM Difference

At the JVM level, BLOCKED and WAITING are distinct in the thread dump output:

  • BLOCKED (on object monitor): The thread is in the entry set of a monitor, waiting to acquire the lock. The dump shows which lock and which thread holds it.
  • WAITING (on object monitor): The thread is in the wait set, having called wait() on that monitor. The dump shows 'waiting on <monitor>' but not who will wake it.
  • WAITING (parking): Thread used LockSupport.park() — typically from java.util.concurrent (e.g., ForkJoinPool workers, CompletableFuture).

The performance impact: A BLOCKED thread consumes no CPU but the OS keeps it in the scheduler's run queue (it's legally runnable but the JVM won't let it). In contrast, a WAITING thread is typically descheduled until notified. Both are 'idle' but the reason matters for debugging.

A thread dump might show hundreds of BLOCKED threads all waiting on the same lock — that's a contention hotspot. WAITING threads on the same condition often indicate a missing notify. WAITING threads with 'parking' are usually normal (thread pool idle).

io/thecodeforge/thread/BlockedVsWaiting.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package io.thecodeforge.thread;

public class BlockedVsWaiting {
    private static final Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        // Thread that holds the lock forever
        Thread holder = new Thread(() -> {
            synchronized (lock) {
                try {
                    Thread.sleep(10000); // holds lock while sleeping
                } catch (InterruptedException e) {}
            }
        }, "holder");

        // Thread that blocks trying to get lock
        Thread blocker = new Thread(() -> {
            synchronized (lock) { // will be BLOCKED
                System.out.println("Never prints");
            }
        }, "blocker");

        // Thread that waits on the same lock (after notify, it would block)
        Thread waiter = new Thread(() -> {
            synchronized (lock) {
                try {
                    lock.wait(); // WAITING
                } catch (InterruptedException e) {}
            }
        }, "waiter");

        holder.start();
        Thread.sleep(100);
        blocker.start();
        waiter.start();
        Thread.sleep(100);

        System.out.println("Holder: " + holder.getState());    // TIMED_WAITING (sleep)
        System.out.println("Blocker: " + blocker.getState());  // BLOCKED
        System.out.println("Waiter: " + waiter.getState());     // WAITING

        System.exit(0);
    }
}
Output
Holder: TIMED_WAITING
Blocker: BLOCKED
Waiter: WAITING
Production Insight
When you see a thread dump with 20+ threads in BLOCKED, don't panic about thread count — panic about which lock. Find the thread that's actually holding it. In a real incident, we traced a BLOCKED cluster to a ThreadPoolExecutor where all worker threads were blocked on a ConcurrentHashMap resize — the map was being resized and took a global lock. That's a known performance pitfall: ConcurrentHashMap's resize blocks all threads briefly.
Rule: BLOCKED threads are always a symptom, not the disease. The real problem is the thread holding the lock.
Key Takeaway
BLOCKED = waiting for a lock (entry set). WAITING = waiting for a signal (wait set).
They are not interchangeable — a WAITING thread gives up its lock, a BLOCKED thread can't even try.
The biggest misdiagnosis: treating a BLOCKED thread as a 'stuck' thread. It's stuck, but the root cause is the lock holder.
Punchline: Always look at the lock owner, not the blocked thread itself.
Diagnose BLOCKED vs WAITING
IfThread is BLOCKED on a monitor held by another thread that's RUNNABLE (but slow)
UseThe holder is doing CPU-intensive work inside the synchronized block. Optimize the critical section or reduce granularity.
IfThread is BLOCKED on a monitor held by a thread that's itself BLOCKED
UseDeadlock. Capture full thread dump with 'jstack -l' and look for threads involved in the cycle.
IfThread is WAITING on a condition with no BLOCKED threads on that monitor
UseMissing notify() or notify() happened before wait(). Check that the notifier sets a boolean flag and that the waiter checks it.
IfThread is WAITING (parking) — often from ForkJoinPool or CompletableFuture
UseNormal idle thread. Nothing to fix unless there's a scalability concern.

Thread Dump Analysis — Reading the Lifecycle in Action

When a production incident hits, your first tool is the thread dump. Here's what you're looking for:

  • Thread name: Often configured in thread pools. 'http-nio-8080-exec-1' indicates a Tomcat worker.
  • State: One of the six above.
  • Stack trace: Shows exactly where the thread is blocked.
  • Lock details: 'waiting for <0x00000007>', 'locked <0x00000008>'. The hex ID identifies the monitor.
  1. Deadlock: Thread A holds lock L1 and wants L2. Thread B holds L2 and wants L1. Both are BLOCKED. The dump explicitly says 'Found one Java-level deadlock'.
  2. Lock contention: Many threads BLOCKED on the same lock, one owner.
  3. Missed signal: Threads WAITING on a condition, nobody holding the lock.
  4. Spinning: Thread state is RUNNABLE but the stack trace shows a tight loop (while(!flag){ } ) — consumes CPU without progress.
dump_analysis_example.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Capture thread dump without killing the process
jstack -l <pid> > /tmp/threaddump_$(date +%s).txt

# Quick scan for BLOCKED threads
grep -E 'BLOCKED|WAITING \(on object monitor\)' /tmp/threaddump_*.txt

# Identify deadlocks explicitly
grep -A 10 'Found one Java-level deadlock' /tmp/threaddump_*.txt

# See which threads hold which locks
grep -E 'locked <|waiting for <' /tmp/threaddump_*.txt

# Count threads per state
awk '/java.lang.Thread.State:/{state=$3; count[state]++} END{for(s in count) print s, count[s]}' /tmp/threaddump_*.txt

# Example output:
# RUNNABLE 12
# BLOCKED 4
# WAITING 3
# TIMED_WAITING 2
# TERMINATED 0
# Expected: healthy pool can have many TIMED_WAITING (idle) and some RUNNABLE
Production Insight
Another gotcha: Thread dumps from a live system over 60 seconds can show the same threads in WAITING state each time. That's expected for idle thread pools. The problem is when threads that should be working are perpetually WAITING — that's a bottleneck upstream.
Key Takeaway
Thread dump analysis is pattern matching: BLOCKED cluster = lock contention; single WAITING thread with no BLOCKED = likely missed notify; many WAITING (park) = idle pool.
Don't trust RUNNABLE as 'busy' — native I/O calls show as RUNNABLE.
Punchline: The state tells you what, the stack trace tells you why.

Common Pitfalls and How to Avoid Them

Even experienced engineers fall into these traps. Here are the most common production failures linked to thread lifecycle misunderstanding:

Pitfall 1: Holding a lock during I/O A synchronized block wrapping a database call or HTTP request. If the external call hangs, every other thread wanting that lock is stuck in BLOCKED. Fix: Move I/O outside the synchronized block, or use a read/write lock, or apply a timeout on the I/O and recheck inside.

Pitfall 2: Notify without state flag Calling notify() but forgetting to set a condition variable that the waiting thread checks. The waiting thread wakes, checks the condition, finds it false, and goes back to WAITING — never to be woken again. Fix: Always use a boolean flag in conjunction with wait/notify.

Pitfall 3: Calling start() twice Thread.start() can only be called once. A second call throws IllegalThreadStateException. This happens often when reusing a thread object. Fix: Create a new Thread instance for each execution.

Pitfall 4: Assuming RUNNABLE means 'working' Resource exhaustion may cause many threads to be in RUNNABLE but not progressing because they're waiting on CPU scheduling. Monitoring tools that only show thread count in RUNNABLE can mislead. Fix: Combine thread dump with CPU profiling (jstack + top -H).

Pitfall 5: Ignoring interrupted flag When InterruptedException is caught, forgetting to restore the interrupt flag (Thread.currentThread().interrupt()) can cause the thread to miss shutdown signals. Fix: Always preserve the interrupt status in catch blocks.

io/thecodeforge/thread/PitfallExample.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package io.thecodeforge.thread;

import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.TimeUnit;

public class PitfallExample {
    private final Lock lock = new ReentrantLock();

    public void doSomethingWithTimeout() throws InterruptedException {
        // Pitfall 1: holding lock during I/O fixed by tryLock with timeout
        if (lock.tryLock(1, TimeUnit.SECONDS)) {
            try {
                // do fast work, then release lock before I/O
                localFastWork();
            } finally {
                lock.unlock();
            }
            // now do I/O without holding lock
            externalCallWithTimeout();
        } else {
            throw new RuntimeException("Could not acquire lock within timeout");
        }
    }

    private void localFastWork() {
        // synchronous fast operations
    }

    private void externalCallWithTimeout() throws InterruptedException {
        // simulate with timeout
        Thread.sleep(200); // normally a URL connection with read timeout
    }

    // Pitfall 3: never call start() twice
    public static void main(String[] args) {
        Thread t = new Thread(() -> {});
        t.start();
        // t.start(); // throws IllegalThreadStateException
        Thread t2 = new Thread(() -> {});
        t2.start(); // always create new thread
    }
}
Output
(No output — demonstrates fix patterns)
Most Common Thread Debugging Mistake
Teams often look at thread dumps and see 'BLOCKED' and assume the blocked threads are the problem. They're wrong. The blocked threads are victims. The culprit is the thread holding the lock. Track the lock owner's stack trace.
Production Insight
I once debugged a system where every HTTP request timed out after 30 seconds. The thread dump showed 50 BLOCKED threads on a ConcurrentHashMap. The lock owner thread was in RUNNABLE but its stack showed it was resizing the map — triggered by a high collision rate due to a flawed hashCode() in a custom key class. The fix was to improve the hashCode(). The lesson: BLOCKED threads can point to unexpected lock holders. Don't assume the lock owner is doing something slow intentionally.
Key Takeaway
Common pitfalls: holding lock during I/O, notify without condition flag, calling start() twice, ignoring interrupt flag.
Each pitfall has a simple fix — but only if you know to look for it.
Punchline: Most thread production bugs are not concurrency complexity — they are missing patterns.
● Production incidentPOST-MORTEMseverity: high

The Case of the Frozen Payment Service

Symptom
Payment API returned HTTP 503 after 30 seconds of no response. The health check endpoint also timed out, but the JVM was still alive with no OOM.
Assumption
The team assumed it was a database pool exhaustion because the symptom looked like slow queries. They restarted the DB connection pool — no effect.
Root cause
A validation thread (part of a custom rule engine) acquired a write lock on a shared configuration map, then performed an HTTP call to an external fraud detection service that had a 60-second timeout. All other threads trying to read the configuration got stuck in BLOCKED state, waiting for that lock.
Fix
1) Replaced the synchronized block with a ReentrantReadWriteLock to allow concurrent reads. 2) Applied a timeout on the external HTTP call (SocketTimeoutException propagated out of the synchronized block). 3) Added circuit breaker around the fraud service call to fail fast when it's down.
Key lesson
  • Never hold a lock during I/O operations — you block all other threads waiting for that lock.
  • Always use timeouts on external calls inside synchronized blocks, or better, avoid blocking I/O entirely when holding locks.
  • On-call engineers need to know how to read thread dumps — the fix was straightforward once they saw the BLOCKED pattern.
Production debug guideSymptom → Action for production thread issues4 entries
Symptom · 01
Thread dump shows many threads in BLOCKED state, all waiting on same monitor
Fix
Find the lock owner thread — it's likely stuck in I/O or deadlocked. Run 'jstack <pid>' and look for the thread holding the lock. Check if it's in RUNNABLE but spinning, or in WAITING. Then either kill it or implement lock timeouts.
Symptom · 02
Application is responsive but requests take 5+ seconds, thread dump shows many WAITING threads on the same condition
Fix
That's often a missing notify()/signal(). Check the code that should wake them. Use 'jstack' to see which thread holds the monitor and what it's doing. Add logging around signal() to confirm it's called.
Symptom · 03
Thread dump shows many threads in RUNNABLE but CPU usage is low
Fix
RUNNABLE can mean 'ready to run' but not currently scheduled — if CPU is low, they're actually waiting for a CPU core. Increase thread pool size or investigate OS scheduling. If they're spinning in a busy-wait (while(!flag)) reduce contention.
Symptom · 04
Periodic latency spikes, thread dump shows threads in TIMED_WAITING on parkNanos()
Fix
Likely a thread pool using timed blocking operations (like take() with timeout). Check if the pool is idle at those times. Adjust keepAliveTime or use synchronous handoff. Also verify that the timed wait is not masking a slow upstream.
★ Quick Thread State Cheat SheetImmediate commands and actions for common thread state issues in production
Service hangs, no progress
Immediate action
Capture thread dump (kill -3 <pid> or jstack <pid>)
Commands
jstack -l <pid> > /tmp/threaddump.txt
grep -E 'BLOCKED|WAITING \(on object monitor\)' /tmp/threaddump.txt | head -20
Fix now
If you see a single thread holding a lock and blocked elsewhere, kill that thread if safe. Otherwise, restart JVM and implement lock timeout.
Slow response, many WAITING threads+
Immediate action
Check if notify() is missing — look for threads in WAITING with same condition
Commands
jstack <pid> | grep -A 30 'WAITING (on object monitor)' | head -60
Check recent code changes around wait/notify or Condition.await/signal
Fix now
If pattern matches known code path, add logging to confirm signal() is called. Otherwise, restart as temporary fix.
Thread State Comparison
StateTriggerCorrective Action (if stuck)
NEWThread created, not startedCall start()
RUNNABLEReady/runningIf stuck and stack shows native I/O, that's expected. If spinning (while loop), add yield or park.
BLOCKEDAwaiting monitor lockFind lock owner; kill or fix it. Reduce critical section size.
WAITINGwait(), join(), park() without timeoutCheck for missing notify(). Ensure condition variable is set before notify.
TIMED_WAITINGsleep, wait(timeout), join(timeout)If stuck, check timeout is not too long. Interrupt if needed.
TERMINATEDrun() completedNo action

Key takeaways

1
Java threads have 6 states
NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED. Each maps to a specific JVM condition.
2
BLOCKED means waiting for a lock (entry set). WAITING means waiting for a signal (wait set). They are not interchangeable.
3
After notify(), the waiting thread must re-acquire the lock before proceeding
it becomes BLOCKED temporarily.
4
Always use a while loop around wait() to handle spurious wakeups and missed signals.
5
Thread dumps are your best friend in production
look for the lock owner, not the blocked threads.
6
Common pitfalls
holding locks during I/O, notify without condition flag, ignoring interrupt flag.

Common mistakes to avoid

4 patterns
×

Treating RUNNABLE as 'actively using CPU'

Symptom
A thread dump shows many RUNNABLE threads but CPU is low. Engineers assume threads are idle—they're actually waiting for CPU scheduling.
Fix
Use 'top -H' to see per-thread CPU usage. A RUNNABLE thread with high CPU shows busy work; low CPU suggests it's just eligible to run.
×

Calling notify() without a condition flag

Symptom
Threads in WAITING never wake up despite notify() being called. The waiting thread checks the condition after waking, finds it false, and goes back to WAITING.
Fix
Always set a boolean flag before notify(). The waiting thread must check that flag in a while loop.
×

Holding a lock during blocking I/O

Symptom
All threads trying to acquire the same lock become BLOCKED, causing application-wide deadlock. The lock owner is stuck in an external call.
Fix
Move I/O outside the synchronized block. Use tryLock with a timeout to fail fast.
×

Ignoring the interrupt flag after InterruptedException

Symptom
The thread cannot be cancelled gracefully. Shutdown hooks or thread.stop() fail to interrupt the thread.
Fix
Always call Thread.currentThread().interrupt() in the catch block to restore the interrupt status.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
Explain the difference between BLOCKED and WAITING thread states in Java...
Q02SENIOR
What happens when a thread calls notify() inside a synchronized block?
Q03SENIOR
How would you debug a scenario where many threads are BLOCKED on the sam...
Q04JUNIOR
Can a thread be in both BLOCKED and WAITING simultaneously?
Q01 of 04SENIOR

Explain the difference between BLOCKED and WAITING thread states in Java.

ANSWER
BLOCKED occurs when a thread tries to enter a synchronized block/method but another thread holds the lock — it's waiting in the entry set. WAITING occurs when a thread voluntarily waits for a signal via wait(), join(), or park() — it's in the wait set. The key difference: BLOCKED threads hold no locks and can't release the one they're waiting for; WAITING threads have released their lock (if any) and are parked until notified.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What is the difference between sleep() and wait() in Java threads?
02
Why does 'jstack' show a thread as RUNNABLE when it's waiting for network I/O?
03
Can a thread move directly from RUNNABLE to TERMINATED without going through WAITING/BLOCKED?
04
What is a spurious wakeup?
05
How can I see thread states in production without killing the process?
🔥

That's Multithreading. Mark it forged?

5 min read · try the examples if you haven't

Previous
Multithreading in Java
2 / 10 · Multithreading
Next
Synchronization in Java