Blocking Calls in Spring Boot – How They Kill Scalability
Most Spring Boot applications do not fail due to CPU or memory shortages. They fail because threads are blocked waiting on I/O.
1. How Spring Boot Handles Requests
In a traditional Spring MVC application:
- One HTTP request = one server thread
- The thread is occupied until the response is returned
- Blocking I/O keeps the thread idle but unavailable
HTTP Request
→ Tomcat Thread
→ Controller
→ Service
→ Blocking DB / HTTP / File IO
(Thread waiting)
2. Common Blocking Calls in Real Applications
| Operation | Why It Blocks |
|---|---|
| JPA / JDBC | Waiting on database response |
| RestTemplate | Synchronous HTTP calls |
| Feign (sync) | Thread-per-request model |
| Thread.sleep() | Explicit thread blocking |
| File I/O | OS-level blocking |
3. The Classic Scalability Failure
@GetMapping("/orders")
public List getOrders() {
return orderService.fetchOrders();
}
public List fetchOrders() {
Thread.sleep(200);
return orderRepository.findAll();
}
With 200 ms latency and 200 threads:
Maximum throughput ≈ 1000 requests/sec, even with idle CPU.
4. Why @Async Alone Does NOT Fix Blocking
@Async
public CompletableFuture> fetchAsync() {
return CompletableFuture.completedFuture(orderRepository.findAll());
}
This:
- Moves blocking to another thread pool
- Adds context switching
- Does not increase throughput
5. Solution #1 – Reduce Blocking at the Source
❌ Chatty Database Access
for (Long id : ids) {
repository.findById(id);
}
✅ Single Query
@Query("select o from Order o where o.id in :ids")
List findAllByIds(List ids);
Fewer DB calls = fewer blocked threads.
6. Solution #2 – Isolate Blocking Work
Dedicated Blocking Thread Pool
@Bean
Executor blockingExecutor() {
return Executors.newFixedThreadPool(50);
}
@Async("blockingExecutor")
public CompletableFuture generateReport() {
return CompletableFuture.completedFuture(reportService.generate());
}
This prevents request thread starvation.
7. Solution #3 – Virtual Threads (Correct Usage)
Enable Virtual Threads
spring.threads.virtual.enabled=true
Or Explicit Executor
@Bean
Executor taskExecutor() {
return Executors.newVirtualThreadPerTaskExecutor();
}
8. Solution #4 – Fix the Database Bottleneck
spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 10
connection-timeout: 30000
Enable JDBC Batching
spring.jpa.properties.hibernate.jdbc.batch_size=50
Database throughput defines system throughput.
9. Solution #5 – Asynchronous Controllers
❌ Blocking Controller
@GetMapping("/data")
public List get() {
return service.fetch();
}
✅ Non-Blocking API
@GetMapping("/data")
public CompletableFuture> get() {
return service.fetchAsync();
}
This releases request threads earlier.
10. When Reactive Actually Makes Sense
Reactive is justified only when:
- End-to-end non-blocking stack
- Streaming or backpressure matters
- R2DBC or reactive clients are used
11. Decision Matrix
| Scenario | Best Choice |
|---|---|
| Blocking DB + MVC | Virtual Threads |
| CPU-heavy work | Bounded Executor |
| Streaming APIs | Reactive |
| Legacy system | Thread isolation |
Final Takeaway
Blocking calls fail silently. Fixing them requires understanding where your threads wait, not just where code executes.
๐ซ Eliminate Blocking Bottlenecks in Spring Boot
Blocking calls are one of the biggest scalability killers in Spring Boot applications. To design truly scalable systems, explore how threading models, database access, and Java 25 concurrency features work together.
⚡ High-Performance Spring Boot with Java 25
Learn how blocking I/O, thread pools, and JVM tuning affect real-world Spring Boot throughput.
๐ Spring Boot Threading & Async Execution
Understand how Spring manages threads, executors, and @Async workloads.
๐️ Spring Boot Database Performance Tuning
See how blocking JDBC calls and connection pools directly impact request throughput.
⚖️ Virtual Threads vs Spring @Async
Learn why virtual threads don’t automatically fix blocking calls in Spring Boot.
๐ Java 25 Virtual Threads – Benchmarks & Pitfalls
Understand the limits of virtual threads when used with blocking I/O.
๐ ️ Spring Boot Performance Tuning & Optimization
End-to-end tuning strategies covering memory, threads, and I/O behavior.
๐งต Java 25 Concurrency (Advanced Interviews)
Prepare for senior interviews discussing blocking vs non-blocking execution models.
๐️ Java System Design Interview Questions
Connect blocking call analysis with real-world scalability and architecture decisions.