Blocking Threads Are the Silent Killer of Java Services

A single slow database call can pin a 200-thread Tomcat pool, and when one endpoint degrades, the whole application grinds to a halt. Reactive programming flips the model: instead of one thread per request, a handful of event-loop threads process thousands of concurrent requests using non-blocking I/O. Three frameworks dominate this space on the JVM — Project Reactor, RxJava, and Eclipse Vert.x — and choosing the wrong one can lock you into an architecture you will fight for years.

TL;DR / Quick Verdict

If you are building with Spring Boot, pick Project Reactor — it is the native engine under Spring WebFlux and integrates with Spring Data, WebClient, and Spring Security with zero glue code. If you need a library-only, tool-agnostic reactive layer with the largest community on the JVM, RxJava 3 is the safe choice. If you want a full application runtime — HTTP server, event bus, circuit breakers, and polyglot support — Vert.x gives you the most out of the box. All three are Apache-2.0 licensed, and all three were pushed to within the last 48 hours as of this writing.

Quick Comparison Table

DimensionProject ReactorRxJava 3Eclipse Vert.x
GitHub stars5,23148,21514,683
Last pushed2026-08-102026-08-102026-08-09
LicenseApache-2.0Apache-2.0Apache-2.0
Core abstractionMono / FluxObservable / FlowableVerticle + event bus
BackpressureNative on both typesFlowable onlyNetty-level + rxified API
HTTP server built inNoNoYes
Polyglot supportNoNoYes (Kotlin, JS, Ruby, etc.)
Spring integrationNative (WebFlux)Via rxjava3 adapterVia vertx-spring-boot
Learning curveMediumMediumSteep
Best forSpring WebFlux appsLibrary-level reactive codeFull reactive microservices

Decision Matrix: Pick in 10 Seconds

Use CaseRecommendedWhy
Spring Boot / WebFlux applicationProject ReactorFirst-party integration; Mono/Flux flow through Spring Data and WebClient
Drop-in reactive layer in plain JavaRxJava 348k-star community, mature operators, works anywhere on the JVM
Reactive microservices with HTTP + eventingVert.xEvent bus and HTTP server built in — no extra web framework needed
Team new to reactive programmingProject ReactorBest documentation and guided learning path; most examples online
High-throughput backpressured pipelinesRxJava 3 (Flowable)Battle-tested backpressure operators from the Reactive Streams lineage

Project Reactor — The Spring-Native Reactive Foundation

Project Reactor (5,231 stars, last push 2026-08-10) describes itself as the “Non-Blocking Reactive Foundation for the JVM.” It implements the Reactive Streams specification with two core types: Mono (zero or one value) and Flux (zero to N values). Both support operators that compose asynchronously, and crucially, both enforce backpressure natively — a slow consumer signals demand upstream, so a fast producer never floods memory.

Reactor is the engine underneath Spring WebFlux, which makes it the default reactive choice for any team already invested in the Spring ecosystem. If you are still weighing web framework options, our Java web frameworks comparison covers Spring Boot, Quarkus, Micronaut, Helidon, and Javalin side by side. A typical example:

1
2
3
4
5
6
7
Flux<Order> orders = orderRepository.findByCustomerId(customerId);
orders
    .filter(o -> o.getStatus() == OrderStatus.PAID)
    .flatMap(o -> inventoryService.reserve(o), 8) // concurrency hint = 8
    .timeout(Duration.ofSeconds(5))
    .onErrorResume(e -> Flux.empty())
    .subscribe(order -> notificationService.send(order));

Reactor shines in three areas: scheduler abstraction (Schedulers.parallel(), Schedulers.boundedElastic()), retry and resilience operators (retryWhen, repeatWhen, timeout), and context propagation (the contextWrite mechanism passes trace IDs through the pipeline without method signatures). Version 3.7 (2026) added improvements to the boundedElastic scheduler and better virtual-thread interop — Spring Boot 3 users can mix virtual threads for blocking work and Reactor for I/O-bound pipelines.

The price you pay: Reactor is opinionated and pulls you toward the Spring ecosystem. Using it outside Spring means adopting reactor-core as a standalone dependency, which works fine but gives you none of the bootstrapping convenience that Spring Boot provides.

RxJava 3 — The Battle-Tested Reactive Extensions

RxJava (48,215 stars, last push 2026-08-10) is the oldest and most battle-tested reactive library on the JVM. It is the Java port of Reactive Extensions (Rx), originally created by Microsoft and ported by Netflix engineers who needed it for their API edge services. RxJava 3.x has been stable since 2018 and is the most widely deployed reactive runtime in production Java code — you will find it inside Android apps, backend services, and even embedded JVM workloads.

RxJava offers five emitter types: Flowable (backpressured, 0..N), Observable (non-backpressured, 0..N), Single (exactly one), Maybe (zero or one), and Completable (completion without value). This granularity lets you model your data shape precisely:

1
2
3
4
5
6
7
8
9
Observable<Event> stream = eventSource.connect();
stream
    .buffer(2, TimeUnit.SECONDS)
    .flatMap(batch -> saveBatch(batch))
    .subscribe(
        result -> log.info("saved {}", result),
        error -> log.error("failed", error),
        () -> log.info("done")
    );

RxJava’s greatest strength is ecosystem neutrality: it depends on nothing, integrates anywhere, and has bindings for Android, gRPC, Retrofit, and dozens of other libraries. Teams pairing RxJava with Retrofit should also see our Java HTTP client libraries comparison — OkHttp, Retrofit, Apache HttpClient, and Feign cover the blocking side of that stack. Its TestScheduler makes deterministic unit testing of time-based operators straightforward, and the operator catalog (300+) is the most comprehensive of the three.

The trade-offs: RxJava 3 development has slowed to a maintenance cadence — the 3.x line receives fixes but few new operators. Backpressure only applies to Flowable, so Observable users must handle slow consumers manually. And because RxJava is unopinionated, you assemble your own threading model — teams new to reactive code often misconfigure subscribeOn/observeOn and end up with mysterious thread-pool leaks.

Eclipse Vert.x — The Reactive Application Toolkit

Vert.x (14,683 stars, last push 2026-08-09) is not a library — it is a toolkit for building reactive applications on the JVM, built on Netty. Instead of composing operators in a stream, you write verticles (self-contained units of work) that communicate over an in-process event bus. Each verticle runs on an event loop, and the model maps naturally to microservices: one verticle for HTTP, one for persistence, one for background processing.

A minimal HTTP server in Vert.x:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Vertx vertx = Vertx.vertx();
vertx.createHttpServer()
    .requestHandler(req -> {
        req.response()
           .putHeader("content-type", "application/json")
           .end("{\"status\":\"ok\"}");
    })
    .listen(8080, result -> {
        if (result.succeeded()) {
            System.out.println("Server listening on 8080");
        }
    });

What makes Vert.x unique: polyglot support (the same runtime runs Java, Kotlin, JavaScript, Ruby, and Groovy verticles), a distributed event bus that can span multiple nodes via clustering, and batteries-included components — HTTP client with pooling, circuit breaker, service discovery, metrics, and an rxified API (io.vertx.rxjava3) that exposes RxJava 3 types for every Vert.x component. For teams building event-driven microservices, Vert.x replaces three separate frameworks (web server, message bus, resilience library) with one coherent runtime.

The cost is complexity: Vert.x expects you to reason in event loops and verticle isolation. A blocking JDBC call inside a verticle will stall its event loop, so you must use the worker verticle pool or the async database client. The learning curve is the steepest of the three, and debugging clustered event-bus flows is genuinely harder than debugging a single reactive chain.

Common Pitfalls When Going Reactive

Blocking calls inside event loops. The number-one production incident in reactive systems is a blocking operation (JDBC, filesystem, sleep) executed on an event-loop thread. In Reactor, route blocking work to boundedElastic; in Vert.x, use worker verticles; in RxJava, use subscribeOn(Schedulers.io()).

Ignoring backpressure. If you subscribe to a Flux/Flowable with a fast producer and a slow consumer, you will buffer until memory dies. Always add explicit demand management — limitRate(), onBackpressureBuffer(capacity), or onBackpressureDrop() with monitoring.

Over-subscription of threads. subscribeOn/observeOn misuse multiplies threads instead of reducing them. The whole point is a small fixed pool — measure with ThreadMXBean or JFR to confirm you are not creating a thread per request.

Swallowing errors. Reactive pipelines propagate errors to the subscriber; a lambda that ignores error -> {} hides failures forever. Route errors to an observability sink (Micrometer timers + counters) so silent drops become visible.

Mixing reactive and blocking stacks. If your team will only adopt reactive code in one service, you get the complexity without the scaling benefit. Consider Vert.x or Reactor only when the whole request path — HTTP through persistence — is non-blocking.

Migration from RxJava 1/2. The rx.Observableio.reactivex.rxjava3.core.Observable package rename breaks every import; use the artifact’s automatic module mapping and a mechanical find-replace, then run the migration test suite before touching business logic.

FAQ

Is Project Reactor better than RxJava?

For Spring Boot and WebFlux applications, yes — Reactor is the first-party reactive engine and integrates with Spring Data, WebClient, and Spring Security natively. As a standalone library, RxJava’s larger ecosystem and neutral design are often the better fit. Both implement the Reactive Streams specification and support backpressure.

Does Vert.x support Spring Boot?

Yes, via the vertx-spring-boot integration, but most teams use Vert.x standalone. It ships its own HTTP server, event bus, and metrics — the Spring integration is mostly for teams that want to adopt Vert.x gradually inside an existing Spring codebase.

Can I use virtual threads instead of reactive programming?

Project Loom’s virtual threads remove the thread-per-request cost, which solves many blocking problems Reactor was designed for. However, virtual threads do not provide backpressure, cancellation, or operator composition. Many 2026 teams use virtual threads for blocking workloads and reactive libraries for streaming and event-driven pipelines — they are complementary, not competing.

Which reactive library has the best performance?

All three are Netty-based (Reactor and Vert.x) or Reactive Streams-compliant (RxJava), and microbenchmarks typically differ by single-digit percentages. Throughput is dominated by operator choice and backpressure discipline, not the library itself. Vert.x can edge ahead in raw HTTP throughput because the server is in-process with zero framework overhead.

Is RxJava still maintained in 2026?

Yes — the 3.x line receives regular commits (last push 2026-08-10) and remains the most widely deployed reactive runtime on the JVM. Development is maintenance-focused: bug fixes and interop improvements rather than new operators. For brand-new projects, evaluate Reactor or Vert.x first; for existing RxJava code, staying on 3.x is perfectly reasonable.

Do I need a reactive database driver?

To get end-to-end non-blocking behavior, yes. R2DBC is the reactive alternative to JDBC for relational databases (supported by Reactor and Spring Data R2DBC); Vert.x ships its own async clients for Postgres, MySQL, MongoDB, and Redis. Using reactive code with blocking JDBC just moves the bottleneck.


💰 想测试你的市场判断力?我用 Polymarket 做预测市场交易——这是全球最大的预测市场平台,从大选结果到技术监管时间线,什么都可以押注。和赌博不同,这是真正的信息市场:你懂的信息越多,胜率越高。我靠预测技术相关事件的走向已经赚了不少。用我的邀请链接注册:Polymarket.com