Thread States

About

Thread states describe the current status of a thread in a Java Virtual Machine (JVM). These states help understand how the application is using threads — whether they are actively working, waiting, blocked, or idle.

Tracking thread states is important in production systems to:

  • Diagnose performance bottlenecks

  • Detect thread leaks

  • Understand concurrency behavior

  • Optimize thread pool configuration

1. NEW

A thread is in the NEW state when it has been created, but its start() method has not been called yet. It is like a plan to start work, but not started.

Technical Characteristics:

  • Exists as a Thread object in memory.

  • It has not been scheduled by the thread scheduler.

  • It consumes almost no system resources (no stack, no OS thread).

Real-World Insight:

In production systems, we rarely observe threads stuck in the NEW state unless:

  • There’s a bug where threads are created but never started.

  • Some code dynamically creates threads but defers their execution (bad practice if overused).

Example: In a job-processing system, creating 100 threads and only starting a few could lead to wasted memory and design flaws.

2. RUNNABLE

This state means the thread is ready to run or is actively running on a CPU. It is under the control of the OS thread scheduler.

Technical Characteristics:

  • The thread may be executing Java bytecode or may be waiting for CPU time.

  • It’s considered “alive” and consuming CPU cycles.

Real-World Insight:

  • Most active business logic runs in this state.

  • High CPU usage is typically due to too many threads in RUNNABLE state.

  • If all threads are RUNNABLE but the system is slow, it might indicate CPU starvation.

Example: A backend service that handles thousands of requests per second will have many threads in the RUNNABLE state doing JSON parsing, DB calls, or business logic.

3. BLOCKED

A thread is BLOCKED when it wants to enter a synchronized block or method, but another thread already holds the lock.

Technical Characteristics:

  • Only one thread can hold a lock on an object at a time.

  • BLOCKED threads are waiting for the lock to be released.

Real-World Insight:

  • This is a sign of thread contention.

  • If many threads are BLOCKED, it can create a bottleneck, reduce throughput, and eventually cause deadlocks or timeouts.

  • This often shows up in thread dumps during production issues.

Example: Two API requests trying to update the same shared cache key at the same time. Only one can hold the lock — others will be BLOCKED, causing latency.

4. WAITING

The thread is waiting indefinitely for another thread to perform an action. Unlike BLOCKED, it’s not trying to acquire a lock; it’s waiting to be notified.

Technical Characteristics:

  • Happens when a thread calls wait(), join(), or LockSupport.park() without timeout.

  • It will remain in this state until explicitly woken up.

Real-World Insight:

  • Used for thread coordination — for example, producer-consumer models.

  • Risk: If the signal (like notify()) is missed, the thread may wait forever — leading to a hung application.

Example: In a workflow engine, a processing thread may WAIT for another thread to complete a prerequisite task. If the upstream thread crashes, the WAITING thread gets stuck forever.

5. TIMED_WAITING

The thread is waiting, but only for a fixed time. After that, it will automatically wake up and continue.

Technical Characteristics:

  • Happens when a thread calls:

    • Thread.sleep(timeout)

    • wait(timeout)

    • join(timeout)

    • LockSupport.parkNanos()

  • It’s a passive wait: the thread isn’t using CPU while waiting.

Real-World Insight:

  • Very common in retry logic, timeouts, backoff strategies, and scheduled jobs.

  • Too many threads stuck in TIMED_WAITING may indicate:

    • Too long of a timeout

    • Poor retry/backoff configuration

    • Slow downstream dependencies

Example: A Spring Boot app calls a third-party payment service with RestTemplate + 5s timeout. If the service is slow, the calling thread sits in TIMED_WAITING for 5 seconds — then either continues or fails.

6. TERMINATED

The thread has finished execution — either completed its task or died due to an unhandled exception.

Technical Characteristics:

  • It cannot be restarted.

  • Memory/resources used by the thread will be released by the JVM.

Real-World Insight:

  • Normally expected after task completion.

  • But if we observe hundreds or thousands of TERMINATED threads, it may indicate:

    • Thread objects are not being garbage collected

    • New threads are being created repeatedly instead of being reused (common bug)

Example: A misconfigured @Async method in Spring that creates a new thread per call without using a thread pool may result in memory pressure and performance degradation, even though threads are TERMINATED.

How to See Thread States in Real Apps

  • Use jstack or a Java profiler to get a thread dump

  • Use Prometheus and Micrometer to monitor thread states over time

  • In Spring Boot, these are often exposed as metrics like:

What to Look For ?

Symptom
Possible Thread State Issue
Diagnosis Strategy

High CPU usage

Many threads in RUNNABLE

Check Prometheus / thread dump

Slowness, latency spikes

Many threads BLOCKED

Investigate shared locks / synchronized

Hanging application

Threads WAITING indefinitely

Check for missed signals or deadlocks

Long retry/wait patterns

Excess TIMED_WAITING threads

Review timeouts, network retries

OutOfMemoryError or crash

Growing TERMINATED threads

Ensure thread reuse / thread pool usage

Thread State in System Design Perspective

Thread states affect:

  • Throughput: How many requests can be handled per second

  • Latency: How fast responses are returned

  • Stability: How resilient our app is under load

That’s why system designers must:

  • Tune thread pools properly

  • Avoid shared locks in high-concurrency paths

  • Choose async patterns when appropriate (e.g., @Async, reactive programming)

  • Use observability tools (e.g., Prometheus, Grafana) to track thread state trends

Tips

  • BLOCKED threads should be investigated first — often signal thread contention or deadlocks.

  • A healthy system typically has a small number of RUNNABLE threads and some TIMED_WAITING or WAITING threads depending on workload.

  • Avoid unbounded thread creation — use thread pools with limits.

  • Sudden spike in any specific thread state could indicate a new issue in code, deployment, or external dependencies.

Scenarios in Spring Boot

WAITING Thread State

Scenario

Why WAITING Happens

Healthy Count

If High, It Might Indicate

@Async method waiting for another async task (Future.get())

Main thread waits for result of background task

Low to moderate

Async task not completing, stuck thread, deadlock

Using Thread.join()

One thread waits for another to finish

Very low

Long-running threads, improper coordination

Java's wait() (e.g., custom lock or queue usage)

Waiting for notify/notifyAll on an object

Low

Missing notify call, misused wait-notify pattern

ExecutorService .awaitTermination()

Thread waiting for pool shutdown

Very low

App is shutting down or hanging on shutdown

Message listener containers (like Kafka or RabbitMQ)

Listener thread may WAIT when idle, waiting for messages

Low to moderate

Normal unless message queue is stuck

Servlet container thread waiting for a response (e.g., HTTP client)

Threads calling a blocking HTTP service and internally using a wait mechanism

Low

Slow downstream service, lack of timeouts

Reactive Spring + non-reactive fallback logic

Sometimes internal blocking code is used for fallback (bad practice in reactive systems)

Should be near zero

Violation of reactive principles — mixing blocking + non-blocking

Last updated