Reactive programming expresses work as asynchronous, nonblocking operations. Instead of waiting for an I/O call to finish, code supplies a continuation that runs when a result or error arrives. The waiting thread can handle other work in the meantime, improving resource use when many requests need database or service calls. section 8.0 section 8.1.2
Figure 8.2 Nonblocking I/O request handling: threads don’t block while waiting for an I/O operation to complete
Compare nonblocking request handling with a thread that waits for I/O: an event-loop thread can handle other work while the operation progresses. section 8.1.1
In Quarkus, a reactive engine built on Vert.x and Netty supports this execution model, but it does not require every method to be reactive. Imperative and reactive code can coexist, allowing a gradual transition. Do not block an event-loop thread: work that must block belongs on a worker thread. Quarkus can choose between them automatically, with @Blocking and @NonBlocking available to adjust that choice. section 1.3.1 section 8.1.1 section 8.7
Reactive database access
A database request illustrates the difference. Traditional Hibernate ORM and JPA use blocking I/O, so an active thread waits for the response. Hibernate Reactive uses reactive database drivers and represents asynchronous results through CompletionStage or SmallRye Mutiny’s Uni (one result) and Multi (multiple results). These types let persistence operations form a reactive pipeline rather than a sequence of synchronous calls. section 7.7
SmallRye Mutiny provides fluent APIs for building pipelines with integrated backpressure handling. Reactive code may take more practice than imperative code; JDK 21 virtual threads offer another way to write imperative code with similar benefits, though they have limitations. section 8.7 section 1.3.1





