Threads
Java threads — creating and managing threads, thread lifecycle, and the concurrency problems every enterprise developer must understand.
Last reviewed: January 2025
🔍 What Is It?
Thread Lifecycle:
NEW -> RUNNABLE -> BLOCKED/WAITING/TIMED_WAITING -> TERMINATED- →NEW — Thread created but not started
- →RUNNABLE — Running or ready to run
- →BLOCKED — Waiting to acquire a monitor lock
- →WAITING — Waiting indefinitely (wait(), join())
- →TIMED_WAITING — Waiting with timeout (sleep(), wait(timeout))
- →TERMINATED — Completed execution
Creating Threads:
- Extend Thread class (not recommended)
- Implement Runnable (better — separates task from execution)
- Use ExecutorService (best — thread pool management)
- Virtual threads (Java 21 — for I/O-bound tasks)
Thread Properties:
- →name — for debugging (set meaningful names!)
- →daemon — daemon threads stop when all non-daemon threads finish
- →priority — hints to scheduler (1-10, rarely useful)
- →interrupt — mechanism to request thread stop
🎯 Why Do We Need It?
Threads enable concurrency:
- →Server applications — handle thousands of simultaneous HTTP requests
- →Background tasks — send emails, process files without blocking the main flow
- →Parallel computation — use all CPU cores for CPU-intensive work
- →Responsive UIs — keep UI thread free while background work runs
- →Asynchronous I/O — don't block waiting for network/disk
🏗️ What Happens in Real Projects?
In Spring Boot, threads are mostly managed by the framework:
- →Each HTTP request runs in a thread from Tomcat's thread pool (default: 200 threads)
- →@Async methods run in a Spring-managed thread pool
- →@Scheduled tasks run in a scheduled thread pool
- →CompletableFuture.supplyAsync() runs in ForkJoinPool
You rarely create threads directly — you use ExecutorService or Spring's abstractions.
⚙️ How It Works
Thread Scheduling:
The JVM delegates thread scheduling to the OS. The OS scheduler decides which thread runs on which CPU core at any time. You cannot control exact scheduling — only set priorities as hints.
Thread Safety Problems:
- Race condition — two threads read-modify-write the same variable concurrently; result depends on timing
- Deadlock — Thread A holds lock1, waits for lock2; Thread B holds lock2, waits for lock1; both wait forever
- Livelock — threads keep responding to each other but make no progress
- Starvation — a thread never gets CPU time because others always have priority
Memory Visibility:
Without synchronization, changes made by one thread may not be visible to another thread due to CPU caches. Use volatile, synchronized, or java.util.concurrent for visibility guarantees.
💻 Example
Thread Patterns in Enterprise Java — How threads are used and managed in production Java applications.
🤔 Common Confusions
❓ What is the difference between start() and run()?
start() creates a new thread and calls run() in that new thread. run() just executes the method in the current thread — no new thread is created. Always call start() to actually run code concurrently. Calling run() directly is a common mistake that looks like multithreading but is actually sequential.
❓ What is the difference between volatile and synchronized?
volatile guarantees visibility — changes to a volatile variable are immediately visible to all threads. It does NOT guarantee atomicity — count++ on a volatile int is still a race condition. synchronized guarantees both visibility AND atomicity — only one thread can execute the synchronized block at a time. Use volatile for simple flags (boolean running = true). Use synchronized or AtomicXxx for compound operations.
❓ What are virtual threads and how are they different from regular threads?
Regular (platform) threads are OS threads — expensive to create (1MB stack each), limited to thousands. Virtual threads (Java 21) are JVM-managed, extremely lightweight — you can create millions. When a virtual thread blocks on I/O, the JVM parks it and runs another virtual thread on the same OS thread. This eliminates the need for reactive/async programming for I/O-bound code.
🚨 Common Production Issues
Deadlock — two threads waiting for each other's locks. Diagnose with jstack. Fix: always acquire locks in the same order.
Thread pool exhaustion — all threads busy, new requests queue up and timeout. Monitor thread pool utilization. Increase pool size or use virtual threads.
ThreadLocal memory leak — ThreadLocal values in thread pools persist across requests if not cleaned up. Always call ThreadLocal.remove() in a finally block.
Uncaught exceptions in threads — exceptions in threads not caught by the caller. Set an UncaughtExceptionHandler or use CompletableFuture which propagates exceptions.
✅ Best Practices
Never create threads directly in production code — use ExecutorService or Spring @Async.
Always name threads meaningfully for debugging: new Thread(task, "order-processor-1").
Use AtomicInteger/AtomicLong instead of synchronized for simple counters.
Use virtual threads (Java 21) for I/O-bound tasks to eliminate thread pool sizing concerns.
Always handle InterruptedException: catch it, restore the interrupt flag, and exit gracefully.
Set UncaughtExceptionHandler on thread pools to log unexpected exceptions.
🏛️ Architect's Note
Java 21 virtual threads are the biggest threading change in Java history. For I/O-bound microservices (most enterprise services), virtual threads eliminate the need for reactive programming (WebFlux, RxJava). You write simple blocking code, but the JVM handles concurrency efficiently. Spring Boot 3.2+ supports virtual threads with spring.threads.virtual.enabled=true. For CPU-bound tasks, platform threads with ForkJoinPool are still the right choice.
💬 Frequently Asked Questions
🎯 Key Takeaways
🔗 Related Topics
🎯 Practice this topic in interviews
Core Java Interview Prep
Practice interview questions that connect directly to this technology track.
↗ Where this topic fits
Core Java Technology Guide
Return to the Core Java learning hub.
Backend Engineer Career Path
A structured journey through Core Java, Spring Boot, production backend engineering, and interview preparation.
Full Stack Java Developer Career Path
A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.
Sources
Sources / References
Use these Core Java references to verify the concept in official docs and continue into the most relevant SplashRide pages.
Official documentation
Related SplashRide pages
SplashRide Core Java learning path
Browse the full Core Java topic collection and learning order on SplashRide.
Core Java Interview Prep
Practice Core Java interview questions and scenario-based answers.
Backend Engineer Career Path
A structured journey through Core Java, Spring Boot, production backend engineering, and interview preparation.
Full Stack Java Developer Career Path
A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.
Runnable
The Runnable interface — the standard way to define a task for concurrent execution in Java.
Executor Framework
The Executor framework — thread pools, ExecutorService types, and how to configure them for production workloads.