Choosing Quasar Fibers or Thread‑per‑Request for High‑Concurrency Java Web Apps
Guide to decide between Quasar fibers and thread‑per‑request for Java web apps, with a comparison table, trade‑offs, and a step‑by‑step Spring Boot integration example.
10 Jan 2026, 14:40 UTC

Decision: Quasar fibers vs thread‑per‑request for a Java web service
You need to decide whether to adopt Quasar’s lightweight fiber‑based concurrency model or stay with the traditional thread‑per‑request approach in a Java‑based web application. The decision hinges on expected request volume, I/O‑bound vs CPU‑bound workloads, existing code‑base compatibility, and operational tooling.
Constraints and assumptions
- Target JVM: Java 8 or later (Quasar requires Java 8+).
- Application stack: Spring Boot 2.x with Hibernate/JPA for data access.
- Primary workload: I/O‑bound (HTTP calls, database queries, external services).
- Desired concurrency: ability to handle hundreds of thousands of simultaneous logical requests with modest memory footprint.
- Operational constraints: existing monitoring relies on standard thread dumps; team prefers minimal learning curve.
Comparison of options
| Aspect | Quasar fibers | Thread‑per‑request |
|---|---|---|
| Memory per concurrent unit | ≈ 1 KB stack (fibers) | ≈ 1 MB stack (OS thread) |
| Context‑switch cost | Low, cooperative | Higher, pre‑emptive |
| Code changes | Minimal; add @Suspendable and wrap blocking calls | None; use existing servlet/controller model |
| Debugging | Requires fiber‑aware tools (e.g., Quasar debugger) | Standard thread dumps, IDE breakpoints |
| CPU‑bound work | Limited benefit; fibers yield only on suspend points | Full core utilization via OS scheduler |
| Library compatibility | Good with Spring, Hibernate; verify third‑party libs for blocking calls | Universal |
Trade‑off explanation
Quasar fibers excel when the application spends most of its time waiting for I/O. Because a fiber’s stack is tiny, you can run many more concurrent logical threads without exhausting heap memory. However, fibers rely on cooperative suspension: any blocking call that is not instrumented with @Suspendable or wrapped in a fiber‑aware block will stall the underlying carrier thread, negating the benefit and adding latency. Debugging also shifts from standard thread dumps to fiber‑aware stack traces, which may require extra tooling.
The thread‑per‑request model is straightforward and works with any blocking library out of the box, but each request consumes a full thread stack, limiting practical concurrency to a few thousand on typical hardware.
Concrete implementation: enabling Quasar in a Spring Boot app
The following steps show how to add Quasar, configure the agent, and expose a simple endpoint that runs inside a fiber. No output is shown; you must verify the behavior yourself.
- Add the dependency (Maven example). Run in your project directory with read/write permissions on
pom.xml:<dependency> <groupId>co.paralleluniverse</groupId> <artifactId>quasar-core</artifactId> <version>0.8.0</version> </dependency> - Enable the Quasar agent when launching the application. The agent instruments classes to detect suspend points. Example command (replace
<app-jar>with your built JAR):
You need execute permission on the agent JAR and read permission on the application JAR.java -javaagent:/path/to/quasar-core-0.8.0.jar -jar <app-jar>.jar - Mark blocking methods as suspendable. For a service that calls a REST client (e.g.,
RestTemplate) add@Suspendable:
If you cannot modify the library, wrap the call in a fiber block:@Service public class RemoteService { @Suspendable public String callRemote() { return restTemplate.getForObject("https://api.example.com/data", String.class); } }@Suspendable public String callRemote() { return Fiber.run(() -> restTemplate.getForObject("https://api.example.com/data", String.class)); } - Expose a controller that runs inside a fiber. Spring MVC already dispatches requests on carrier threads; you can delegate to a fiber:
@RestController public class DemoController { @Autowired private RemoteService remoteService; @GetMapping("/fiber-demo") public ResponseEntity<String> demo() { String result = Fiber.run(() -> remoteService.callRemote()); return ResponseEntity.ok(result); } } - Validate memory usage. Create a simple load test (e.g., using
heyorwrk) that sends many concurrent requests and observe heap usage viajcmd <pid> VM.native_memory summaryor a profiling tool. Expected observation: with Quasar, the native memory footprint stays low (a few MB) even with 100k concurrent logical requests, whereas a thread‑per‑request baseline would show a steep rise. - Check for missing suspend points. Run the application with the system property
-Dco.paralleluniverse.fibers.verifySuspendable=true. If a blocking call is not marked suspendable, the JVM will throw anIllegalStateExceptionindicating a missing suspend point. Fix by adding@Suspendableor wrapping the call.
Limitations and practical verification
- CPU‑bound tasks (e.g., heavy image processing) do not gain from fibers; consider off‑loading to a separate thread pool.
- Fiber stacks are not visible in standard
jstackoutput; use Quasar’sFiber.dump()or a IDE plugin for debugging. - Ensure all third‑party libraries that perform blocking I/O are either fiber‑aware or wrapped; otherwise you may experience carrier‑thread stalls and increased latency.
- Verify Hibernate session handling: open a session inside a fiber block and close it before the fiber ends to avoid session leaks.
Practical way to check the result: after deploying the fiber‑enabled service, run a steady‑state load test for 5 minutes with a target concurrency of 50 k requests. Monitor java.lang.management.MemoryMXBean heap and non‑heap usage. If heap stays below a configured threshold (e.g., 500 MB) and response latency remains stable, the fiber configuration is behaving as expected. If you see rising latency or frequent IllegalStateException about missing suspend points, review your code for un‑instrumented blocking calls.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.