Introduction
Imagine you’re running a delivery service.
- Some deliveries are predictable and steady
- Some are sudden and short-lived
- Some must be handled one at a time
- Some must run every day at 9 AM
Would you hire the same type of workers for all of these tasks? Of course not.
👉 Java thread pools work exactly the same way.
Each thread pool type is designed for a specific workload pattern. Using the wrong one can lead to:
- Performance issues
- Resource exhaustion
- Unpredictable behavior in production
In this blog, you’ll learn:
- All major Java thread pool types
- When to use each one
- A single Spring Boot app demonstrating all of them
- End-to-end REST APIs, with curl requests and responses
Core Concepts: Thread Pools in Simple Terms
A thread pool is a reusable group of threads managed by Java’s ExecutorService.
Instead of creating threads manually:
- Tasks are submitted
- Threads pick tasks from a queue
- Threads are reused
This gives you:
✔ Better performance
✔ Controlled concurrency
✔ Safer multithreading
Thread Pool Types You Must Know
| Thread Pool | When to Use |
|---|---|
| FixedThreadPool | Stable number of tasks |
| CachedThreadPool | Short-lived async tasks |
| SingleThreadExecutor | Sequential execution |
| ScheduledThreadPool | Delayed or periodic tasks |
Let’s build one Spring Boot app that demonstrates all four.
End-to-End Setup (Spring Boot + Java 21)
Maven Dependencies
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
Thread Pool Configuration
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@Configuration
public class ThreadPoolConfig {
@Bean
public ExecutorService fixedThreadPool() {
return Executors.newFixedThreadPool(3);
}
@Bean
public ExecutorService cachedThreadPool() {
return Executors.newCachedThreadPool();
}
@Bean
public ExecutorService singleThreadExecutor() {
return Executors.newSingleThreadExecutor();
}
@Bean
public ScheduledExecutorService scheduledThreadPool() {
return Executors.newScheduledThreadPool(2);
}
}
1️⃣ FixedThreadPool – Stable Workload
When to Use
- Known number of concurrent tasks
- Predictable traffic
- Backend processing
REST API Example
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.ExecutorService;
@RestController
public class FixedThreadPoolController {
private final ExecutorService fixedThreadPool;
public FixedThreadPoolController(ExecutorService fixedThreadPool) {
this.fixedThreadPool = fixedThreadPool;
}
@GetMapping("/fixed")
public String processFixedTasks() {
for (int i = 1; i <= 5; i++) {
int taskId = i;
fixedThreadPool.submit(() ->
System.out.println("Fixed Task " + taskId +
" executed by " + Thread.currentThread().getName())
);
}
return "Fixed thread pool tasks submitted";
}
}
CURL
curl http://localhost:8080/fixed
Response
Fixed thread pool tasks submitted
2️⃣ CachedThreadPool – Short-Lived Async Tasks
When to Use
- Burst traffic
- Short tasks
- Lightweight async work
⚠️ Be careful: unbounded threads
REST API Example
@RestController
public class CachedThreadPoolController {
private final ExecutorService cachedThreadPool;
public CachedThreadPoolController(ExecutorService cachedThreadPool) {
this.cachedThreadPool = cachedThreadPool;
}
@GetMapping("/cached")
public String processCachedTasks() {
for (int i = 1; i <= 5; i++) {
cachedThreadPool.submit(() ->
System.out.println("Cached task by " +
Thread.currentThread().getName())
);
}
return "Cached thread pool tasks submitted";
}
}
CURL
curl http://localhost:8080/cached
Response
Cached thread pool tasks submitted
3️⃣ SingleThreadExecutor – Sequential Execution
When to Use
- Order must be preserved
- One task at a time
- Logging, auditing, batch steps
REST API Example
@RestController
public class SingleThreadController {
private final ExecutorService singleThreadExecutor;
public SingleThreadController(ExecutorService singleThreadExecutor) {
this.singleThreadExecutor = singleThreadExecutor;
}
@GetMapping("/single")
public String processSequentialTasks() {
for (int i = 1; i <= 3; i++) {
int taskId = i;
singleThreadExecutor.submit(() ->
System.out.println("Sequential task " + taskId +
" executed by " + Thread.currentThread().getName())
);
}
return "Single-thread tasks submitted";
}
}
CURL
curl http://localhost:8080/single
Response
Single-thread tasks submitted
✔ Tasks always execute in order
4️⃣ ScheduledThreadPool – Delayed or Periodic Tasks
When to Use
- Scheduled jobs
- Polling
- Cleanup tasks
REST API Example
@RestController
public class ScheduledThreadPoolController {
private final ScheduledExecutorService scheduledExecutor;
public ScheduledThreadPoolController(ScheduledExecutorService scheduledExecutor) {
this.scheduledExecutor = scheduledExecutor;
}
@GetMapping("/schedule")
public String scheduleTask() {
scheduledExecutor.schedule(
() -> System.out.println("Scheduled task executed"),
5,
java.util.concurrent.TimeUnit.SECONDS
);
return "Task scheduled to run after 5 seconds";
}
}
CURL
curl http://localhost:8080/schedule
Response
Task scheduled to run after 5 seconds
Best Practices for Thread Pools
✅ 1. Match Pool Type to Workload
Wrong pool = production issue.
✅ 2. Prefer FixedThreadPool for APIs
Predictable and safe.
✅ 3. Always Shutdown Executors (on app exit)
Avoid memory leaks.
❌ Common Mistakes to Avoid
🚫 Using CachedThreadPool for heavy traffic
🚫 Blocking threads unnecessarily
🚫 Ignoring queue behavior
🚫 Creating thread pools per request
Conclusion
Thread pools are not interchangeable.
Each type exists for a reason:
- Fixed → controlled concurrency
- Cached → short async bursts
- Single → strict ordering
- Scheduled → time-based execution
Understanding when—and why—to use each one is a must-have skill for every Java developer.
Call to Action 🚀
💬 Which thread pool do you use most in real projects?
Ask questions or share experiences in the comments.
📌 Follow for more Java programming, concurrency, and Spring Boot deep dives.
🔗 Authoritative References
- Oracle ExecutorService Docs https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/ExecutorService.html
- Java Concurrency Package https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/package-summary.html
- Java 21 Documentation https://docs.oracle.com/en/java/javase/21/
Top comments (0)