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
Threadobject 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(), orLockSupport.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
@Asyncmethod 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
jstackor a Java profiler to get a thread dumpUse Prometheus and Micrometer to monitor thread states over time
In Spring Boot, these are often exposed as metrics like:
What to Look For ?
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